66 Account const dpIssuer(
"dpIssuer");
67 char const credType[] =
"KYC_VERIFIED";
71 env(credentials::create(subject, dpIssuer, credType));
73 env(credentials::accept(subject, dpIssuer, credType));
75 auto const jv = credentials::ledgerEntry(env, subject, dpIssuer, credType);
76 return jv[jss::result][jss::index].asString();
81 Env env(*
this, features);
85 {{.account = bob, .payAmount = 100, .convertAmount = 50},
86 {.account = carol, .payAmount = 100, .convertAmount = 50}}};
87 auto& mpt = confEnv.
mpt;
88 env(fset(bob, asfDepositAuth));
100 env(deposit::auth(bob, carol));
114 env(deposit::unauth(bob, carol));
128 Env env(*
this, features);
129 env.fund(XRP(50000), dpIssuer);
135 {{.account = bob, .payAmount = 100, .convertAmount = 50},
136 {.account = carol, .payAmount = 100, .convertAmount = 50}}};
137 auto& mpt = confEnv.
mpt;
138 env(fset(bob, asfDepositAuth));
141 auto const credIdx = createCredential(env, carol);
148 .credentials = {{credIdx}},
153 env(deposit::authCredentials(bob, {{.issuer = dpIssuer, .credType = credType}}));
165 mpt.send({.account = carol, .dest = bob, .amt = 10, .credentials = {{credIdx}}});
173 Env env(*
this, features);
174 env.fund(XRP(50000), dpIssuer);
180 {{.account = bob, .payAmount = 100, .convertAmount = 50},
181 {.account = carol, .payAmount = 100, .convertAmount = 50}}};
182 auto& mpt = confEnv.
mpt;
183 env(fset(bob, asfDepositAuth));
186 auto const credIdx = createCredential(env, carol);
189 env(deposit::auth(bob, carol));
203 mpt.send({.account = carol, .dest = bob, .amt = 10, .credentials = {{credIdx}}});
209 env(deposit::unauth(bob, carol));
225 .credentials = {{credIdx}},
230 env(deposit::authCredentials(bob, {{.issuer = dpIssuer, .credType = credType}}));
234 mpt.send({.account = carol, .dest = bob, .amt = 10, .credentials = {{credIdx}}});
237 auto const expireTime = 30;
241 auto createExpiringCredential = [&](Env& env,
Account const& subject) ->
std::string {
242 auto jv = credentials::create(subject, dpIssuer, credType);
244 env.current()->header().parentCloseTime.time_since_epoch().count() + expireTime;
245 jv[sfExpiration.jsonName] = expiry;
248 env(credentials::accept(subject, dpIssuer, credType));
250 auto const credentials = credentials::ledgerEntry(env, subject, dpIssuer, credType);
251 return credentials[jss::result][jss::index].asString();
254 auto credentialDeleted = [&](Env& env,
Account const& subject) ->
bool {
255 auto const credentials = credentials::ledgerEntry(env, subject, dpIssuer, credType);
256 return credentials[jss::result].isMember(jss::error) &&
257 credentials[jss::result][jss::error] ==
"entryNotFound";
266 Env env(*
this, features);
267 env.fund(XRP(50000), dpIssuer);
273 {{.account = bob, .payAmount = 100, .convertAmount = 50},
274 {.account = carol, .payAmount = 100, .convertAmount = 50}}};
275 auto& mpt = confEnv.
mpt;
276 env(fset(bob, asfDepositAuth));
279 auto const credIdx = createExpiringCredential(env, carol);
282 env(deposit::authCredentials(bob, {{.issuer = dpIssuer, .credType = credType}}));
293 .credentials = {{credIdx}},
298 BEAST_EXPECT(credentialDeleted(env, carol));
307 Env env(*
this, features);
308 env.fund(XRP(50000), dpIssuer);
314 {{.account = bob, .payAmount = 100, .convertAmount = 50},
315 {.account = carol, .payAmount = 100, .convertAmount = 50}}};
316 auto& mpt = confEnv.
mpt;
318 auto const credIdx = createExpiringCredential(env, carol);
328 .credentials = {{credIdx}},
333 BEAST_EXPECT(credentialDeleted(env, carol));
348 Env env(*
this, features);
349 env.fund(XRP(50000), dpIssuer);
355 {{.account = bob, .payAmount = 100, .convertAmount = 50},
356 {.account = carol, .payAmount = 100, .convertAmount = 50}}};
357 auto& mpt = confEnv.
mpt;
358 env(fset(bob, asfDepositAuth));
361 auto const credIdx = createExpiringCredential(env, carol);
371 .credentials = {{credIdx}},
377 BEAST_EXPECT(!credentialDeleted(env, carol));
384 testcase(
"Send credential validation");
403 Account const dpIssuer(
"dpIssuer");
404 char const credType[] =
"KYC";
408 Env env(*
this, features);
412 {{.account = bob, .payAmount = 100, .convertAmount = 50},
413 {.account = carol, .payAmount = 100, .convertAmount = 50}}};
414 auto& mpt = confEnv.
mpt;
427 Env env(*
this, features);
431 {{.account = bob, .payAmount = 100, .convertAmount = 50},
432 {.account = carol, .payAmount = 100, .convertAmount = 50}}};
433 auto& mpt = confEnv.
mpt;
437 for (
int i = 0; i < 9; ++i)
444 .credentials = tooManyCredentials,
451 Env env(*
this, features);
452 env.fund(XRP(50000), dpIssuer);
457 {{.account = bob, .payAmount = 100, .convertAmount = 50},
458 {.account = carol, .payAmount = 100, .convertAmount = 50}}};
459 auto& mpt = confEnv.
mpt;
461 env(credentials::create(carol, dpIssuer, credType));
463 env(credentials::accept(carol, dpIssuer, credType));
466 auto const jv = credentials::ledgerEntry(env, carol, dpIssuer, credType);
467 std::string const credIdx = jv[jss::result][jss::index].asString();
473 .credentials = {{credIdx, credIdx}},
480 Env env(*
this, features);
484 {{.account = bob, .payAmount = 100, .convertAmount = 50},
485 {.account = carol, .payAmount = 100, .convertAmount = 50}}};
486 auto& mpt = confEnv.
mpt;
493 .credentials = {{fakeCredIdx}},
500 Env env(*
this, features);
501 env.fund(XRP(50000), dpIssuer);
506 {{.account = bob, .payAmount = 100, .convertAmount = 50},
507 {.account = carol, .payAmount = 100, .convertAmount = 50}}};
508 auto& mpt = confEnv.
mpt;
511 env(credentials::create(bob, dpIssuer, credType));
513 env(credentials::accept(bob, dpIssuer, credType));
516 auto const jv = credentials::ledgerEntry(env, bob, dpIssuer, credType);
517 std::string const credIdx = jv[jss::result][jss::index].asString();
523 .credentials = {{credIdx}},
530 Env env(*
this, features);
531 env.fund(XRP(50000), dpIssuer);
536 {{.account = bob, .payAmount = 100, .convertAmount = 50},
537 {.account = carol, .payAmount = 100, .convertAmount = 50}}};
538 auto& mpt = confEnv.
mpt;
541 env(credentials::create(carol, dpIssuer, credType));
544 auto const jv = credentials::ledgerEntry(env, carol, dpIssuer, credType);
545 std::string const credIdx = jv[jss::result][jss::index].asString();
551 .credentials = {{credIdx}},
561 Env env(*
this, features - featureCredentials);
565 {{.account = bob, .payAmount = 100, .convertAmount = 50},
566 {.account = carol, .payAmount = 100, .convertAmount = 50}}};
567 auto& mpt = confEnv.
mpt;
569 auto constexpr kCredIdx =
570 "48004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288BE4";
576 .credentials = {{kCredIdx}},
592 testcase(
"AMM holder cannot have confidential state");
598 for (
bool const enablePseudoAccount : {
false,
true})
602 enablePseudoAccount ? features | featureSingleAssetVault
603 : features - featureSingleAssetVault};
605 MPTTester mptAlice(env, alice, {.holders = {bob}});
608 .flags = kMptDexFlags | tfMPTCanClawback | tfMPTCanHoldConfidentialBalance,
610 mptAlice.authorize({.account = bob});
611 mptAlice.pay(alice, bob, 1'000);
613 mptAlice.generateKeyPair(alice);
614 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
616 AMM
const amm(env, bob, XRP(100), mptAlice(100));
617 Account const ammHolder(
"amm", amm.ammAccount());
620 BEAST_EXPECT(ammSle && ammSle->isFieldPresent(sfAMMID));
621 BEAST_EXPECT(mptAlice.getBalance(ammHolder) == 100);
623 BEAST_EXPECT(!mptAlice.getEncryptedBalance(ammHolder, MPTTester::holderEncryptedInbox));
625 !mptAlice.getEncryptedBalance(ammHolder, MPTTester::holderEncryptedSpending));
627 !mptAlice.getEncryptedBalance(ammHolder, MPTTester::issuerEncryptedBalance));
629 !mptAlice.getEncryptedBalance(ammHolder, MPTTester::auditorEncryptedBalance));
631 mptAlice.confidentialClaw({
648 testcase(
"Full AMMWithdraw with an unrelated holder's COA");
655 for (
bool const withFix : {
true,
false})
657 for (
bool const carolConverts : {
false,
true})
659 Env env{*
this, withFix ? features | fixCleanup3_5_0 : features - fixCleanup3_5_0};
661 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
662 mptAlice.create({.flags = kMptDexFlags | tfMPTCanHoldConfidentialBalance});
663 mptAlice.authorize({.account = bob});
664 mptAlice.authorize({.account = carol});
665 mptAlice.pay(alice, bob, 1'000);
666 mptAlice.pay(alice, carol, 100);
668 mptAlice.generateKeyPair(alice);
669 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
673 AMM amm(env, bob, XRP(100), mptAlice(100));
674 Account const ammHolder(
"amm", amm.ammAccount());
675 BEAST_EXPECT(amm.ammExists());
676 BEAST_EXPECT(mptAlice.getBalance(ammHolder) == 100);
682 !mptAlice.getEncryptedBalance(ammHolder, MPTTester::holderEncryptedInbox));
684 !mptAlice.getEncryptedBalance(ammHolder, MPTTester::holderEncryptedSpending));
686 !mptAlice.getEncryptedBalance(ammHolder, MPTTester::issuerEncryptedBalance));
688 !mptAlice.getEncryptedBalance(ammHolder, MPTTester::auditorEncryptedBalance));
692 mptAlice.generateKeyPair(carol);
694 {.account = carol, .amt = 1, .holderPubKey = mptAlice.getPubKey(carol)});
697 BEAST_EXPECT(mptAlice.getIssuanceConfidentialBalance() == (carolConverts ? 1 : 0));
699 if (carolConverts && !withFix)
706 BEAST_EXPECT(amm.ammExists());
707 BEAST_EXPECT(mptAlice.getBalance(ammHolder) == 100);
715 auto const bobLPTokens = amm.getLPTokensBalance(bob.id());
717 bob,
IOUAmount{bobLPTokens.mantissa() / 2, bobLPTokens.exponent()});
719 BEAST_EXPECT(amm.ammExists());
720 BEAST_EXPECT(mptAlice.getBalance(ammHolder) > 0);
727 amm.withdrawAll(bob);
730 BEAST_EXPECT(!amm.ammExists());
745 testcase(
"Confidential transfer with tickets");
748 Env env{*
this, features};
752 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
756 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
758 mptAlice.authorize({.account = bob});
759 mptAlice.authorize({.account = carol});
760 mptAlice.pay(alice, bob, 100);
761 mptAlice.pay(alice, carol, 100);
763 mptAlice.generateKeyPair(alice);
764 mptAlice.generateKeyPair(bob);
765 mptAlice.generateKeyPair(carol);
770 env(ticket::create(alice, 1));
771 mptAlice.set({.issuerPubKey = mptAlice.getPubKey(alice), .ticketSeq = ticketSeq});
777 env(ticket::create(bob, 1));
781 .holderPubKey = mptAlice.getPubKey(bob),
782 .ticketSeq = ticketSeq,
784 env.require(MptBalance(mptAlice, bob, 50));
790 env(ticket::create(bob, 1));
791 mptAlice.convert({.account = bob, .amt = 20, .ticketSeq = ticketSeq});
792 env.require(MptBalance(mptAlice, bob, 30));
798 env(ticket::create(bob, 1));
799 mptAlice.mergeInbox({.account = bob, .ticketSeq = ticketSeq});
802 mptAlice.convert({.account = carol, .amt = 50, .holderPubKey = mptAlice.getPubKey(carol)});
803 mptAlice.mergeInbox({.account = carol});
808 env(ticket::create(bob, 1));
809 mptAlice.send({.account = bob, .dest = carol, .amt = 10, .ticketSeq = ticketSeq});
813 mptAlice.mergeInbox({.account = carol});
819 env(ticket::create(carol, 1));
820 mptAlice.convertBack({.account = carol, .amt = 10, .ticketSeq = ticketSeq});
822 env.require(MptBalance(mptAlice, carol, 60));
832 testcase(
"Convert proof binds to ticket sequence");
835 Env env{*
this, features};
838 MPTTester mptAlice(env, alice, {.holders = {bob}});
843 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
845 mptAlice.authorize({.account = bob});
846 mptAlice.pay(alice, bob, 100);
848 mptAlice.generateKeyPair(alice);
849 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
850 mptAlice.generateKeyPair(bob);
852 uint64_t
const amt = 30;
854 Buffer const holderCt = mptAlice.encryptAmount(bob, amt, bf);
855 Buffer const issuerCt = mptAlice.encryptAmount(alice, amt, bf);
858 env(ticket::create(bob, 1));
863 BEAST_EXPECT(env.seq(bob) != ticketSeq1);
867 mptAlice.getSchnorrProof(bob, badCtxHash),
"Missing Schnorr Proof.");
872 .proof =
strHex(badProof),
873 .holderPubKey = mptAlice.getPubKey(bob),
874 .holderEncryptedAmt = holderCt,
875 .issuerEncryptedAmt = issuerCt,
876 .blindingFactor = bf,
877 .ticketSeq = ticketSeq1,
883 env(ticket::create(bob, 1));
889 .holderPubKey = mptAlice.getPubKey(bob),
890 .holderEncryptedAmt = holderCt,
891 .issuerEncryptedAmt = issuerCt,
892 .blindingFactor = bf,
893 .ticketSeq = ticketSeq2,
895 env.require(MptBalance(mptAlice, bob, 70));
1008 testcase(
"Batch confidential send - merge inbox dependency");
1012 Env env{*
this, features};
1018 MPTTester mpt(env, alice, {.holders = {bob, carol, dave}});
1026 auto const bobSeq = env.seq(bob);
1027 auto const carolSeq = env.seq(carol);
1029 auto const batchFee = batch::calcConfidentialBatchFee(env, 1, 3);
1031 auto const jv1 = mpt.sendJV({.account = bob, .dest = carol, .amt = 100}, bobSeq + 1);
1032 auto const jv2 = mpt.mergeInboxJV({.account = carol});
1033 auto const jv3 = mpt.sendJV({.account = carol, .dest = dave, .amt = 50}, carolSeq + 1);
1035 env(batch::outer(bob, bobSeq, batchFee, tfAllOrNothing),
1036 batch::Inner(jv1, bobSeq + 1),
1037 batch::Inner(jv2, carolSeq),
1038 batch::Inner(jv3, carolSeq + 1),
1045 BEAST_EXPECT(mpt.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending) == 100);
1046 BEAST_EXPECT(mpt.getDecryptedBalance(carol, MPTTester::holderEncryptedInbox) == 0);
1054 Env env{*
this, features};
1060 MPTTester mpt(env, alice, {.holders = {bob, carol, dave}});
1065 auto const bobSeq = env.seq(bob);
1066 auto const batchFee = batch::calcConfidentialBatchFee(env, 0, 2);
1068 auto const jv1 = mpt.sendJV({.account = bob, .dest = carol, .amt = 50}, bobSeq + 1);
1069 auto const jv2 = mpt.sendJV({.account = bob, .dest = dave, .amt = 60}, bobSeq + 2);
1071 env(batch::outer(bob, bobSeq, batchFee, tfAllOrNothing),
1072 batch::Inner(jv1, bobSeq + 1),
1073 batch::Inner(jv2, bobSeq + 2),
1079 mpt.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending) == 150);
1080 BEAST_EXPECT(mpt.getDecryptedBalance(carol, MPTTester::holderEncryptedInbox) == 0);
1081 BEAST_EXPECT(mpt.getDecryptedBalance(dave, MPTTester::holderEncryptedInbox) == 0);
1086 auto const bobSeq = env.seq(bob);
1087 auto const batchFee = batch::calcConfidentialBatchFee(env, 0, 2);
1089 auto const jv1 = mpt.sendJV({.account = bob, .dest = carol, .amt = 50}, bobSeq + 1);
1090 auto const jv2 = mpt.sendJV({.account = bob, .dest = dave, .amt = 60}, bobSeq + 2);
1092 env(batch::outer(bob, bobSeq, batchFee, tfIndependent),
1093 batch::Inner(jv1, bobSeq + 1),
1094 batch::Inner(jv2, bobSeq + 2),
1100 mpt.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending) == 100);
1101 BEAST_EXPECT(mpt.getDecryptedBalance(carol, MPTTester::holderEncryptedInbox) == 50);
1103 BEAST_EXPECT(mpt.getDecryptedBalance(dave, MPTTester::holderEncryptedInbox) == 0);
1112 Env env{*
this, features};
1118 MPTTester mpt(env, alice, {.holders = {bob, carol, dave}});
1122 auto const bobSeq = env.seq(bob);
1123 auto const batchFee = batch::calcConfidentialBatchFee(env, 0, 2);
1127 mpt.sendJV({.account = bob, .dest = carol, .amt = 100}, bobSeq + 1);
1130 auto const chain1 = mpt.chainAfterSend(bob, 100, jv1);
1134 mpt.sendJV({.account = bob, .dest = dave, .amt = 100}, bobSeq + 2, chain1);
1136 env(batch::outer(bob, bobSeq, batchFee, tfAllOrNothing),
1137 batch::Inner(jv1, bobSeq + 1),
1138 batch::Inner(jv2, bobSeq + 2),
1143 BEAST_EXPECT(mpt.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending) == 0);
1145 mpt.getDecryptedBalance(carol, MPTTester::holderEncryptedInbox) == 100);
1146 BEAST_EXPECT(mpt.getDecryptedBalance(dave, MPTTester::holderEncryptedInbox) == 100);
1152 Env env2{*
this, features};
1153 Account const alice2(
"alice");
1155 Account const carol2(
"carol");
1158 MPTTester mpt2(env2, alice2, {.holders = {bob2, carol2, dave2}});
1161 auto const bobSeq = env2.seq(bob2);
1162 auto const batchFee = batch::calcConfidentialBatchFee(env2, 0, 2);
1165 mpt2.sendJV({.account = bob2, .dest = carol2, .amt = 100}, bobSeq + 1);
1166 auto const chain1 = mpt2.chainAfterSend(bob2, 100, jv1);
1169 mpt2.sendJV({.account = bob2, .dest = dave2, .amt = 100}, bobSeq + 2, chain1);
1172 batch::outer(bob2, bobSeq, batchFee, tfAllOrNothing),
1173 batch::Inner(jv1, bobSeq + 1),
1174 batch::Inner(jv2, bobSeq + 2),
1180 mpt2.getDecryptedBalance(bob2, MPTTester::holderEncryptedSpending) == 150);
1182 mpt2.getDecryptedBalance(carol2, MPTTester::holderEncryptedInbox) == 0);
1183 BEAST_EXPECT(mpt2.getDecryptedBalance(dave2, MPTTester::holderEncryptedInbox) == 0);
1190 testcase(
"Batch confidential convert and convertBack");
1202 Env env{*
this, features};
1208 MPTTester mpt(env, alice, {.holders = {bob, carol, dave}});
1212 mpt.pay(alice, bob, 50);
1214 auto const bobSeq = env.seq(bob);
1215 auto const batchFee = batch::calcConfidentialBatchFee(env, 0, 2);
1218 auto const jv1 = mpt.convertJV({.account = bob, .amt = 50}, bobSeq + 1);
1220 auto const jv2 = mpt.convertBackJV({.account = bob, .amt = 30}, bobSeq + 2);
1222 env(batch::outer(bob, bobSeq, batchFee, tfAllOrNothing),
1223 batch::Inner(jv1, bobSeq + 1),
1224 batch::Inner(jv2, bobSeq + 2),
1231 env.require(MptBalance(mpt, bob, 30));
1232 BEAST_EXPECT(mpt.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending) == 70);
1233 BEAST_EXPECT(mpt.getDecryptedBalance(bob, MPTTester::holderEncryptedInbox) == 50);
1245 Env env{*
this, features};
1251 MPTTester mpt(env, alice, {.holders = {bob, carol, dave}});
1253 mpt.pay(alice, bob, 50);
1255 auto const bobSeq = env.seq(bob);
1256 auto const batchFee = batch::calcConfidentialBatchFee(env, 0, 3);
1258 auto const jv1 = mpt.convertJV({.account = bob, .amt = 50}, bobSeq + 1);
1259 auto const jv2 = mpt.mergeInboxJV({.account = bob});
1261 auto const jv3 = mpt.convertBackJV({.account = bob, .amt = 30}, bobSeq + 3);
1263 env(batch::outer(bob, bobSeq, batchFee, tfAllOrNothing),
1264 batch::Inner(jv1, bobSeq + 1),
1265 batch::Inner(jv2, bobSeq + 2),
1266 batch::Inner(jv3, bobSeq + 3),
1271 env.require(MptBalance(mpt, bob, 50));
1272 BEAST_EXPECT(mpt.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending) == 100);
1273 BEAST_EXPECT(mpt.getDecryptedBalance(bob, MPTTester::holderEncryptedInbox) == 0);
1282 testcase(
"Batch confidential mixed operations");
1298 Env env{*
this, features};
1304 MPTTester mpt(env, alice, {.holders = {bob, carol, dave}});
1309 mpt.pay(alice, carol, 50);
1311 mpt.pay(alice, dave, 50);
1312 mpt.convert({.account = dave, .amt = 50});
1313 mpt.mergeInbox({.account = dave});
1315 auto const bobSeq = env.seq(bob);
1316 auto const carolSeq = env.seq(carol);
1317 auto const daveSeq = env.seq(dave);
1319 auto const batchFee = batch::calcConfidentialBatchFee(env, 2, 4);
1322 auto const jv1 = mpt.sendJV({.account = bob, .dest = carol, .amt = 30}, bobSeq + 1);
1324 auto const jv2 = mpt.convertJV({.account = carol, .amt = 50}, carolSeq);
1326 auto const jv3 = mpt.convertBackJV({.account = dave, .amt = 20}, daveSeq);
1329 auto const jv4 = mpt.mergeInboxJV({.account = carol});
1331 env(batch::outer(bob, bobSeq, batchFee, tfAllOrNothing),
1332 batch::Inner(jv1, bobSeq + 1),
1333 batch::Inner(jv2, carolSeq),
1334 batch::Inner(jv3, daveSeq),
1335 batch::Inner(jv4, carolSeq + 1),
1336 batch::Sig(carol, dave),
1341 BEAST_EXPECT(mpt.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending) == 70);
1343 BEAST_EXPECT(mpt.getDecryptedBalance(carol, MPTTester::holderEncryptedSpending) == 80);
1344 BEAST_EXPECT(mpt.getDecryptedBalance(carol, MPTTester::holderEncryptedInbox) == 0);
1346 BEAST_EXPECT(mpt.getDecryptedBalance(dave, MPTTester::holderEncryptedSpending) == 30);
1347 env.require(MptBalance(mpt, dave, 20));
1356 Env env{*
this, features};
1362 MPTTester mpt(env, alice, {.holders = {bob, carol, dave}});
1365 auto const bobSeq = env.seq(bob);
1366 auto const batchFee = batch::calcConfidentialBatchFee(env, 0, 2);
1369 auto const jv1 = mpt.sendJV({.account = bob, .dest = carol, .amt = 30}, bobSeq + 1);
1371 auto const jv2 = mpt.convertBackJV({.account = bob, .amt = 40}, bobSeq + 2);
1373 env(batch::outer(bob, bobSeq, batchFee, tfAllOrNothing),
1374 batch::Inner(jv1, bobSeq + 1),
1375 batch::Inner(jv2, bobSeq + 2),
1380 BEAST_EXPECT(mpt.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending) == 100);
1381 BEAST_EXPECT(mpt.getDecryptedBalance(carol, MPTTester::holderEncryptedInbox) == 0);
1390 testcase(
"Batch confidential MPT - all or nothing");
1393 Env env{*
this, features};
1399 MPTTester mpt(env, alice, {.holders = {bob, carol, dave}});
1405 auto const bobSeq = env.seq(bob);
1406 auto const carolSeq = env.seq(carol);
1407 auto const batchFee = batch::calcConfidentialBatchFee(env, 1, 2);
1409 auto const jv1 = mpt.sendJV({.account = bob, .dest = dave, .amt = 10}, bobSeq + 1);
1410 auto const jv2 = mpt.sendJV({.account = carol, .dest = dave, .amt = 5}, carolSeq);
1412 env(batch::outer(bob, bobSeq, batchFee, tfAllOrNothing),
1413 batch::Inner(jv1, bobSeq + 1),
1414 batch::Inner(jv2, carolSeq),
1420 BEAST_EXPECT(mpt.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending) == 90);
1421 BEAST_EXPECT(mpt.getDecryptedBalance(carol, MPTTester::holderEncryptedSpending) == 55);
1422 BEAST_EXPECT(mpt.getDecryptedBalance(dave, MPTTester::holderEncryptedInbox) == 15);
1429 testcase(
"Batch confidential MPT - only one");
1432 Env env{*
this, features};
1438 MPTTester mpt(env, alice, {.holders = {bob, carol, dave}});
1444 auto const bobSeq = env.seq(bob);
1445 auto const carolSeq = env.seq(carol);
1446 auto const batchFee = batch::calcConfidentialBatchFee(env, 1, 2);
1449 auto const jv1 = mpt.sendJV({.account = bob, .dest = dave, .amt = 200}, bobSeq + 1);
1450 auto const jv2 = mpt.sendJV({.account = carol, .dest = dave, .amt = 300}, carolSeq);
1452 env(batch::outer(bob, bobSeq, batchFee, tfOnlyOne),
1453 batch::Inner(jv1, bobSeq + 1),
1454 batch::Inner(jv2, carolSeq),
1460 BEAST_EXPECT(mpt.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending) == 100);
1461 BEAST_EXPECT(mpt.getDecryptedBalance(carol, MPTTester::holderEncryptedSpending) == 60);
1462 BEAST_EXPECT(mpt.getDecryptedBalance(dave, MPTTester::holderEncryptedInbox) == 0);
1467 auto const bobSeq = env.seq(bob);
1468 auto const carolSeq = env.seq(carol);
1469 auto const batchFee = batch::calcConfidentialBatchFee(env, 1, 2);
1471 auto jv1 = mpt.sendJV({.account = bob, .dest = dave, .amt = 200}, bobSeq + 1);
1472 auto jv2 = mpt.sendJV({.account = carol, .dest = dave, .amt = 5}, carolSeq);
1474 env(batch::outer(bob, bobSeq, batchFee, tfOnlyOne),
1475 batch::Inner(jv1, bobSeq + 1),
1476 batch::Inner(jv2, carolSeq),
1482 BEAST_EXPECT(mpt.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending) == 100);
1483 BEAST_EXPECT(mpt.getDecryptedBalance(carol, MPTTester::holderEncryptedSpending) == 55);
1484 BEAST_EXPECT(mpt.getDecryptedBalance(dave, MPTTester::holderEncryptedInbox) == 5);
1491 testcase(
"Batch confidential MPT - until failure");
1494 Env env{*
this, features};
1500 MPTTester mpt(env, alice, {.holders = {bob, carol, dave}});
1507 auto const bobSeq = env.seq(bob);
1508 auto const carolSeq = env.seq(carol);
1509 auto const batchFee = batch::calcConfidentialBatchFee(env, 1, 2);
1511 auto const jv1 = mpt.sendJV({.account = bob, .dest = dave, .amt = 200}, bobSeq + 1);
1512 auto const jv2 = mpt.sendJV({.account = carol, .dest = dave, .amt = 5}, carolSeq);
1514 env(batch::outer(bob, bobSeq, batchFee, tfUntilFailure),
1515 batch::Inner(jv1, bobSeq + 1),
1516 batch::Inner(jv2, carolSeq),
1521 BEAST_EXPECT(mpt.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending) == 100);
1522 BEAST_EXPECT(mpt.getDecryptedBalance(carol, MPTTester::holderEncryptedSpending) == 60);
1527 auto const bobSeq = env.seq(bob);
1528 auto const carolSeq = env.seq(carol);
1529 auto const batchFee = batch::calcConfidentialBatchFee(env, 1, 2);
1531 auto const jv1 = mpt.sendJV({.account = bob, .dest = dave, .amt = 10}, bobSeq + 1);
1532 auto const jv2 = mpt.sendJV({.account = carol, .dest = dave, .amt = 5}, carolSeq);
1534 env(batch::outer(bob, bobSeq, batchFee, tfUntilFailure),
1535 batch::Inner(jv1, bobSeq + 1),
1536 batch::Inner(jv2, carolSeq),
1542 BEAST_EXPECT(mpt.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending) == 90);
1543 BEAST_EXPECT(mpt.getDecryptedBalance(carol, MPTTester::holderEncryptedSpending) == 55);
1544 BEAST_EXPECT(mpt.getDecryptedBalance(dave, MPTTester::holderEncryptedInbox) == 15);
1551 testcase(
"Batch confidential MPT - independent");
1554 Env env{*
this, features};
1560 MPTTester mpt(env, alice, {.holders = {bob, carol, dave}});
1568 auto const bobSeq = env.seq(bob);
1569 auto const carolSeq = env.seq(carol);
1570 auto const batchFee = batch::calcConfidentialBatchFee(env, 1, 3);
1572 auto const jv1 = mpt.sendJV({.account = bob, .dest = dave, .amt = 10}, bobSeq + 1);
1575 auto const jv2 = mpt.sendJV({.account = carol, .dest = dave, .amt = 300}, carolSeq);
1576 auto const jv3 = mpt.sendJV({.account = carol, .dest = dave, .amt = 5}, carolSeq + 1);
1578 env(batch::outer(bob, bobSeq, batchFee, tfIndependent),
1579 batch::Inner(jv1, bobSeq + 1),
1580 batch::Inner(jv2, carolSeq),
1581 batch::Inner(jv3, carolSeq + 1),
1587 BEAST_EXPECT(mpt.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending) == 90);
1589 BEAST_EXPECT(mpt.getDecryptedBalance(carol, MPTTester::holderEncryptedSpending) == 55);
1591 BEAST_EXPECT(mpt.getDecryptedBalance(dave, MPTTester::holderEncryptedInbox) == 15);
1603 testcase(
"Batch confidential MPT with tickets");
1609 Env env{*
this, features};
1615 MPTTester mpt(env, alice, {.holders = {bob, carol, dave}});
1620 env(ticket::create(bob, 1));
1623 auto const bobSeq = env.seq(bob);
1625 auto const batchFee = batch::calcConfidentialBatchFee(env, 0, 2);
1629 auto const jv1 = mpt.sendJV({.account = bob, .dest = carol, .amt = 40}, bobSeq);
1630 auto const chain1 = mpt.chainAfterSend(bob, 40, jv1);
1632 mpt.sendJV({.account = bob, .dest = dave, .amt = 20}, bobSeq + 1, chain1);
1634 env(batch::outer(bob, 0, batchFee, tfAllOrNothing),
1635 batch::Inner(jv1, bobSeq),
1636 batch::Inner(jv2, bobSeq + 1),
1637 ticket::Use(outerTicketSeq),
1642 BEAST_EXPECT(mpt.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending) == 40);
1643 BEAST_EXPECT(mpt.getDecryptedBalance(carol, MPTTester::holderEncryptedInbox) == 40);
1644 BEAST_EXPECT(mpt.getDecryptedBalance(dave, MPTTester::holderEncryptedInbox) == 20);
1651 Env env{*
this, features};
1657 MPTTester mpt(env, alice, {.holders = {bob, carol, dave}});
1663 env(ticket::create(bob, 2));
1666 auto const bobSeq = env.seq(bob);
1667 auto const batchFee = batch::calcConfidentialBatchFee(env, 0, 2);
1670 auto const jv1 = mpt.sendJV({.account = bob, .dest = carol, .amt = 40}, ticketSeq1);
1672 auto const chain1 = mpt.chainAfterSend(bob, 40, jv1);
1674 mpt.sendJV({.account = bob, .dest = dave, .amt = 30}, ticketSeq2, chain1);
1676 env(batch::outer(bob, bobSeq, batchFee, tfAllOrNothing),
1677 batch::Inner(jv1, 0, ticketSeq1),
1678 batch::Inner(jv2, 0, ticketSeq2),
1683 BEAST_EXPECT(mpt.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending) == 30);
1684 BEAST_EXPECT(mpt.getDecryptedBalance(carol, MPTTester::holderEncryptedInbox) == 40);
1685 BEAST_EXPECT(mpt.getDecryptedBalance(dave, MPTTester::holderEncryptedInbox) == 30);
1690 Env env{*
this, features};
1696 MPTTester mpt(env, alice, {.holders = {bob, carol, dave}});
1700 env(ticket::create(bob, 1));
1703 auto const bobSeq = env.seq(bob);
1704 auto const batchFee = batch::calcConfidentialBatchFee(env, 0, 2);
1707 auto const badJV = mpt.sendJV({.account = bob, .dest = carol, .amt = 40}, bobSeq + 1);
1708 auto const jv2 = mpt.mergeInboxJV({.account = bob});
1710 env(batch::outer(bob, bobSeq, batchFee, tfAllOrNothing),
1711 batch::Inner(badJV, 0, ticketSeq),
1712 batch::Inner(jv2, bobSeq + 1),
1716 BEAST_EXPECT(mpt.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending) == 100);
1717 BEAST_EXPECT(mpt.getDecryptedBalance(carol, MPTTester::holderEncryptedInbox) == 0);
1727 testcase(
"Confidential transfers through delegation");
1730 Env env{*
this, features};
1736 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
1737 env.fund(XRP(10000), dave);
1742 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanClawback |
1743 tfMPTCanHoldConfidentialBalance,
1745 mptAlice.authorize({.account = bob});
1746 mptAlice.authorize({.account = carol});
1747 mptAlice.pay(alice, bob, 200);
1748 mptAlice.pay(alice, carol, 100);
1750 mptAlice.generateKeyPair(alice);
1751 mptAlice.generateKeyPair(bob);
1752 mptAlice.generateKeyPair(carol);
1753 mptAlice.set({.issuerPubKey = mptAlice.getPubKey(alice)});
1757 env(delegate::set(bob, dave, {
"ConfidentialMPTConvert"}), Ter(
temMALFORMED));
1761 env(delegate::set(bob, dave, {
"ConfidentialMPTMergeInbox"}));
1769 .holderPubKey = mptAlice.getPubKey(bob),
1778 .holderPubKey = mptAlice.getPubKey(bob),
1780 env.require(MptBalance(mptAlice, bob, 100));
1783 mptAlice.convert({.account = bob, .amt = 50});
1786 mptAlice.mergeInbox({.account = bob, .delegate = dave});
1792 .holderPubKey = mptAlice.getPubKey(carol),
1794 mptAlice.mergeInbox({.account = carol});
1805 env(delegate::set(bob, dave, {
"ConfidentialMPTMergeInbox",
"ConfidentialMPTSend"}));
1809 mptAlice.send({.account = bob, .dest = carol, .amt = 10, .delegate = dave});
1810 mptAlice.mergeInbox({.account = carol});
1813 mptAlice.convertBack(
1820 {
"ConfidentialMPTMergeInbox",
"ConfidentialMPTSend",
"ConfidentialMPTConvertBack"}));
1824 mptAlice.convertBack({.account = bob, .amt = 10, .delegate = dave});
1827 mptAlice.confidentialClaw(
1831 env(delegate::set(alice, dave, {
"ConfidentialMPTClawback"}));
1835 mptAlice.confidentialClaw({.holder = bob, .amt = 130, .delegate = dave});
1842 testcase(
"Confidential delegation revocation");
1845 Env env{*
this, features};
1850 MPTTester mptAlice(env, alice, {.holders = {bob}});
1851 env.fund(XRP(10000), carol);
1856 .flags = tfMPTCanTransfer | tfMPTCanHoldConfidentialBalance,
1858 mptAlice.authorize({.account = bob});
1859 mptAlice.pay(alice, bob, 100);
1861 mptAlice.generateKeyPair(alice);
1862 mptAlice.generateKeyPair(bob);
1863 mptAlice.set({.issuerPubKey = mptAlice.getPubKey(alice)});
1866 auto const bobOwnersBefore =
ownerCount(env, bob);
1867 env(delegate::set(bob, carol, {
"ConfidentialMPTMergeInbox"}));
1869 env.require(Owners(bob, bobOwnersBefore + 1));
1875 .holderPubKey = mptAlice.getPubKey(bob),
1877 mptAlice.mergeInbox({.account = bob, .delegate = carol});
1882 env.require(Owners(bob, bobOwnersBefore));
1885 mptAlice.convert({.account = bob, .amt = 30});
1888 mptAlice.mergeInbox({
1895 mptAlice.mergeInbox({.account = bob});
1903 testcase(
"Confidential delegation with auditor");
1906 Env env{*
this, features};
1911 Account const auditor{
"auditor"};
1913 MPTTester mptAlice(env, alice, {.holders = {bob, carol}, .auditor = auditor});
1914 env.fund(XRP(10000), dave);
1919 .flags = tfMPTCanTransfer | tfMPTCanHoldConfidentialBalance,
1921 mptAlice.authorize({.account = bob});
1922 mptAlice.authorize({.account = carol});
1923 mptAlice.pay(alice, bob, 100);
1924 mptAlice.pay(alice, carol, 100);
1926 mptAlice.generateKeyPair(alice);
1927 mptAlice.generateKeyPair(bob);
1928 mptAlice.generateKeyPair(carol);
1929 mptAlice.generateKeyPair(auditor);
1931 .issuerPubKey = mptAlice.getPubKey(alice),
1932 .auditorPubKey = mptAlice.getPubKey(auditor),
1936 env(delegate::set(bob, dave, {
"ConfidentialMPTSend"}));
1943 .holderPubKey = mptAlice.getPubKey(bob),
1945 mptAlice.mergeInbox({.account = bob});
1950 .holderPubKey = mptAlice.getPubKey(carol),
1952 mptAlice.mergeInbox({.account = carol});
1955 mptAlice.send({.account = bob, .dest = carol, .amt = 20, .delegate = dave});
1956 mptAlice.send({.account = bob, .dest = carol, .amt = 10, .delegate = dave});
1959 env(delegate::set(bob, auditor, {
"ConfidentialMPTSend",
"ConfidentialMPTConvertBack"}));
1963 mptAlice.send({.account = bob, .dest = carol, .amt = 10, .delegate = auditor});
1964 mptAlice.convertBack({.account = bob, .amt = 10, .delegate = auditor});
2027 testcase(
"Batch ConfidentialMPTSend with delegation");
2033 Env env{*
this, features};
2039 MPTTester mpt(env, alice, {.holders = {bob, carol, dave}});
2042 env(delegate::set(bob, dave, {
"ConfidentialMPTSend"}));
2045 auto const bobSeq = env.seq(bob);
2046 auto const batchFee = batch::calcConfidentialBatchFee(env, 1, 2);
2049 auto jv1 = mpt.sendJV({.account = bob, .dest = carol, .amt = 60}, bobSeq + 1);
2050 jv1[jss::Delegate] = dave.human();
2052 auto jv2 = mpt.sendJV({.account = bob, .dest = dave, .amt = 60}, bobSeq + 2);
2053 jv2[jss::Delegate] = dave.human();
2055 env(batch::outer(bob, bobSeq, batchFee, tfAllOrNothing),
2056 batch::Inner(jv1, bobSeq + 1),
2057 batch::Inner(jv2, bobSeq + 2),
2063 BEAST_EXPECT(mpt.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending) == 100);
2064 BEAST_EXPECT(mpt.getDecryptedBalance(carol, MPTTester::holderEncryptedInbox) == 0);
2065 BEAST_EXPECT(mpt.getDecryptedBalance(dave, MPTTester::holderEncryptedInbox) == 0);
2070 Env env{*
this, features};
2076 MPTTester mpt(env, alice, {.holders = {bob, carol, dave}});
2079 env(delegate::set(bob, dave, {
"ConfidentialMPTSend"}));
2082 auto const bobSeq = env.seq(bob);
2083 auto const batchFee = batch::calcConfidentialBatchFee(env, 1, 2);
2086 auto jv1 = mpt.sendJV({.account = bob, .dest = carol, .amt = 40}, bobSeq + 1);
2087 jv1[jss::Delegate] = dave.human();
2088 auto const chain1 = mpt.chainAfterSend(bob, 40, jv1);
2090 auto jv2 = mpt.sendJV({.account = bob, .dest = dave, .amt = 40}, bobSeq + 2, chain1);
2091 jv2[jss::Delegate] = dave.human();
2093 env(batch::outer(bob, bobSeq, batchFee, tfAllOrNothing),
2094 batch::Inner(jv1, bobSeq + 1),
2095 batch::Inner(jv2, bobSeq + 2),
2101 BEAST_EXPECT(mpt.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending) == 20);
2102 BEAST_EXPECT(mpt.getDecryptedBalance(carol, MPTTester::holderEncryptedInbox) == 40);
2103 BEAST_EXPECT(mpt.getDecryptedBalance(dave, MPTTester::holderEncryptedInbox) == 40);
2111 testcase(
"Batch delegation missing permission");
2117 Env env{*
this, features};
2123 MPTTester mpt(env, alice, {.holders = {bob, carol, dave}});
2127 env(delegate::set(bob, dave, {
"ConfidentialMPTMergeInbox"}));
2130 auto const bobSeq = env.seq(bob);
2131 auto const carolSeq = env.seq(carol);
2132 auto const batchFee = batch::calcConfidentialBatchFee(env, 2, 2);
2135 auto const jv1 = mpt.sendJV({.account = carol, .dest = dave, .amt = 30}, carolSeq);
2137 auto jv2 = mpt.sendJV({.account = bob, .dest = carol, .amt = 50}, bobSeq + 1);
2138 jv2[jss::Delegate] = dave.human();
2140 env(batch::outer(bob, bobSeq, batchFee, tfAllOrNothing),
2141 batch::Inner(jv1, carolSeq),
2142 batch::Inner(jv2, bobSeq + 1),
2143 batch::Sig(carol, dave),
2149 BEAST_EXPECT(mpt.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending) == 100);
2150 BEAST_EXPECT(mpt.getDecryptedBalance(carol, MPTTester::holderEncryptedSpending) == 60);
2151 BEAST_EXPECT(mpt.getDecryptedBalance(dave, MPTTester::holderEncryptedInbox) == 0);
2157 Env env{*
this, features};
2163 MPTTester mpt(env, alice, {.holders = {bob, carol, dave}});
2167 auto const bobSeq = env.seq(bob);
2168 auto const carolSeq = env.seq(carol);
2169 auto const batchFee = batch::calcConfidentialBatchFee(env, 2, 2);
2171 auto jv1 = mpt.sendJV({.account = bob, .dest = carol, .amt = 50}, bobSeq + 1);
2172 jv1[jss::Delegate] = dave.human();
2173 auto const jv2 = mpt.sendJV({.account = carol, .dest = dave, .amt = 30}, carolSeq);
2175 env(batch::outer(bob, bobSeq, batchFee, tfIndependent),
2176 batch::Inner(jv1, bobSeq + 1),
2177 batch::Inner(jv2, carolSeq),
2178 batch::Sig(carol, dave),
2183 BEAST_EXPECT(mpt.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending) == 100);
2184 BEAST_EXPECT(mpt.getDecryptedBalance(carol, MPTTester::holderEncryptedSpending) == 30);
2185 BEAST_EXPECT(mpt.getDecryptedBalance(dave, MPTTester::holderEncryptedInbox) == 30);
2193 testcase(
"Test batch delegated send with delegate as outer account");
2200 Env env{*
this, features};
2206 MPTTester mpt(env, alice, {.holders = {bob, carol, dave}});
2209 env(delegate::set(bob, dave, {
"ConfidentialMPTSend"}));
2212 auto const daveSeq = env.seq(dave);
2213 auto const bobSeq = env.seq(bob);
2214 auto const batchFee = batch::calcConfidentialBatchFee(env, 0, 2);
2216 auto jv1 = mpt.sendJV({.account = bob, .dest = carol, .amt = 40}, bobSeq);
2217 jv1[jss::Delegate] = dave.human();
2218 auto const jv2 = mpt.mergeInboxJV({.account = dave});
2220 env(batch::outer(dave, daveSeq, batchFee, tfAllOrNothing),
2221 batch::Inner(jv1, bobSeq),
2222 batch::Inner(jv2, daveSeq + 1),
2226 BEAST_EXPECT(mpt.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending) == 60);
2227 BEAST_EXPECT(mpt.getDecryptedBalance(carol, MPTTester::holderEncryptedInbox) == 40);
2233 Env env{*
this, features};
2239 MPTTester mpt(env, alice, {.holders = {bob, carol, dave}});
2242 env(delegate::set(bob, dave, {
"ConfidentialMPTSend"}));
2245 auto const daveSeq = env.seq(dave);
2246 auto const bobSeq = env.seq(bob);
2247 auto const batchFee = batch::calcConfidentialBatchFee(env, 1, 2);
2249 auto const jv1 = mpt.sendJV({.account = bob, .dest = carol, .amt = 40}, bobSeq);
2250 auto const chain1 = mpt.chainAfterSend(bob, 40, jv1);
2251 auto jv2 = mpt.sendJV({.account = bob, .dest = carol, .amt = 30}, bobSeq + 1, chain1);
2252 jv2[jss::Delegate] = dave.human();
2255 env(batch::outer(dave, daveSeq, batchFee, tfAllOrNothing),
2256 batch::Inner(jv1, bobSeq),
2257 batch::Inner(jv2, bobSeq + 1),
2263 BEAST_EXPECT(mpt.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending) == 30);
2264 BEAST_EXPECT(mpt.getDecryptedBalance(carol, MPTTester::holderEncryptedInbox) == 70);
2272 Env env{*
this, features};
2278 MPTTester mpt(env, alice, {.holders = {bob, carol, dave}});
2282 auto const daveSeq = env.seq(dave);
2283 auto const bobSeq = env.seq(bob);
2284 auto const carolSeq = env.seq(carol);
2285 auto const batchFee = batch::calcConfidentialBatchFee(env, 2, 2);
2287 auto jv1 = mpt.sendJV({.account = bob, .dest = carol, .amt = 50}, bobSeq);
2288 jv1[jss::Delegate] = dave.human();
2289 auto const jv2 = mpt.sendJV({.account = carol, .dest = dave, .amt = 30}, carolSeq);
2291 env(batch::outer(dave, daveSeq, batchFee, tfAllOrNothing),
2292 batch::Inner(jv1, bobSeq),
2293 batch::Inner(jv2, carolSeq),
2294 batch::Sig(bob, carol),
2299 BEAST_EXPECT(mpt.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending) == 100);
2300 BEAST_EXPECT(mpt.getDecryptedBalance(carol, MPTTester::holderEncryptedSpending) == 60);
2301 BEAST_EXPECT(mpt.getDecryptedBalance(dave, MPTTester::holderEncryptedInbox) == 0);
2309 testcase(
"Batch delegated confidential multiple operations");
2312 Env env{*
this, features};
2320 MPTTester mpt(env, alice, {.holders = {bob, carol, dave, frank}});
2322 mpt.pay(alice, bob, 50);
2323 env.fund(XRP(10000), erin);
2326 mpt.authorize({.account = frank});
2327 mpt.pay(alice, frank, 40);
2328 mpt.generateKeyPair(frank);
2330 env(delegate::set(bob, dave, {
"ConfidentialMPTConvertBack"}));
2331 env(delegate::set(carol, erin, {
"ConfidentialMPTSend"}));
2332 env(delegate::set(bob, erin, {
"ConfidentialMPTMergeInbox"}));
2335 auto const daveSeq = env.seq(dave);
2336 auto const bobSeq = env.seq(bob);
2337 auto const carolSeq = env.seq(carol);
2338 auto const frankSeq = env.seq(frank);
2339 auto const batchFee = batch::calcConfidentialBatchFee(env, 4, 6);
2345 auto jv1 = mpt.convertBackJV({.account = bob, .amt = 30}, bobSeq);
2346 jv1[jss::Delegate] = dave.human();
2347 auto const jv2 = mpt.convertJV({.account = bob, .amt = 20}, bobSeq + 1);
2348 auto jv3 = mpt.sendJV({.account = carol, .dest = bob, .amt = 15}, carolSeq);
2349 jv3[jss::Delegate] = erin.human();
2350 auto const jv4 = mpt.convertJV(
2351 {.account = frank, .amt = 25, .holderPubKey = mpt.getPubKey(frank)}, frankSeq);
2352 auto const jv5 = mpt.mergeInboxJV({.account = frank});
2353 auto jv6 = mpt.mergeInboxJV({.account = bob});
2354 jv6[jss::Delegate] = erin.human();
2356 env(batch::outer(dave, daveSeq, batchFee, tfAllOrNothing),
2357 batch::Inner(jv1, bobSeq),
2358 batch::Inner(jv2, bobSeq + 1),
2359 batch::Inner(jv3, carolSeq),
2360 batch::Inner(jv4, frankSeq),
2361 batch::Inner(jv5, frankSeq + 1),
2362 batch::Inner(jv6, bobSeq + 2),
2363 batch::Sig(erin, frank, bob),
2367 env.require(MptBalance(mpt, bob, 60));
2368 BEAST_EXPECT(mpt.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending) == 105);
2369 BEAST_EXPECT(mpt.getDecryptedBalance(bob, MPTTester::holderEncryptedInbox) == 0);
2370 env.require(MptBalance(mpt, carol, 0));
2371 BEAST_EXPECT(mpt.getDecryptedBalance(carol, MPTTester::holderEncryptedSpending) == 45);
2372 BEAST_EXPECT(mpt.getDecryptedBalance(carol, MPTTester::holderEncryptedInbox) == 0);
2373 env.require(MptBalance(mpt, frank, 15));
2374 BEAST_EXPECT(mpt.getDecryptedBalance(frank, MPTTester::holderEncryptedSpending) == 25);
2375 BEAST_EXPECT(mpt.getDecryptedBalance(frank, MPTTester::holderEncryptedInbox) == 0);
2376 env.require(MptBalance(mpt, dave, 0));
2377 BEAST_EXPECT(mpt.getDecryptedBalance(dave, MPTTester::holderEncryptedSpending) == 0);
2378 BEAST_EXPECT(mpt.getDecryptedBalance(dave, MPTTester::holderEncryptedInbox) == 0);
2379 auto const outstandingBalance = mpt.getIssuanceOutstandingBalance();
2380 BEAST_EXPECT(outstandingBalance && *outstandingBalance == 250);
2381 BEAST_EXPECT(mpt.getIssuanceConfidentialBalance() == 175);
2391 testcase(
"Invalid cases for delegation with tickets");
2394 Env env{*
this, features};
2398 MPTTester mptAlice(env, alice, {.holders = {bob}});
2399 env.fund(XRP(10000), carol);
2404 .flags = tfMPTCanTransfer | tfMPTCanHoldConfidentialBalance | tfMPTCanClawback,
2406 mptAlice.authorize({.account = bob});
2407 mptAlice.pay(alice, bob, 200);
2409 mptAlice.generateKeyPair(alice);
2410 mptAlice.generateKeyPair(bob);
2411 mptAlice.set({.issuerPubKey = mptAlice.getPubKey(alice)});
2414 mptAlice.convert({.account = bob, .amt = 100, .holderPubKey = mptAlice.getPubKey(bob)});
2415 mptAlice.mergeInbox({.account = bob});
2418 env(delegate::set(bob, carol, {
"ConfidentialMPTConvertBack"}));
2421 uint64_t
const amt = 10;
2426 mptAlice.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending),
2427 "Missing spending balance.");
2429 mptAlice.getEncryptedBalance(bob, MPTTester::holderEncryptedSpending),
2430 "Missing encrypted spending balance.");
2431 auto const version = mptAlice.getMPTokenVersion(bob);
2433 auto const pc = mptAlice.getPedersenCommitment(spendingBalance, pcBf);
2437 return mptAlice.getConvertBackProof(
2442 .pedersenCommitment = pc,
2443 .amt = spendingBalance,
2444 .encryptedAmt = encSpending,
2445 .blindingFactor = pcBf,
2451 auto const ticketSeq = env.seq(bob) + 1;
2452 env(ticket::create(bob, 1));
2454 mptAlice.convertBack({
2457 .proof = proofForSeq(ticketSeq + 1),
2458 .pedersenCommitment = pc,
2460 .ticketSeq = ticketSeq,
2467 auto const ticketSeq = env.seq(bob) + 1;
2468 env(ticket::create(bob, 1));
2470 mptAlice.convertBack({
2473 .proof = proofForSeq(env.seq(bob)),
2474 .pedersenCommitment = pc,
2476 .ticketSeq = ticketSeq,
2483 mptAlice.convertBack({
2487 .ticketSeq = env.seq(bob) + 100,
2494 mptAlice.convertBack({
2506 auto const carolTicketSeq = env.seq(carol) + 1;
2507 env(ticket::create(carol, 1));
2509 mptAlice.convertBack({
2513 .ticketSeq = carolTicketSeq,
2521 auto const ticketSeq = env.seq(bob) + 1;
2522 env(ticket::create(bob, 1));
2525 mptAlice.convertBack({
2528 .proof = proofForSeq(ticketSeq),
2529 .pedersenCommitment = pc,
2539 auto const ticketSeq = env.seq(bob) + 1;
2540 env(ticket::create(bob, 1));
2542 mptAlice.convertBack({
2546 .ticketSeq = ticketSeq,
2557 testcase(
"Confidential delegation with tickets");
2560 Env env{*
this, features};
2565 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
2566 env.fund(XRP(10000), dave);
2571 .flags = tfMPTCanTransfer | tfMPTCanHoldConfidentialBalance | tfMPTCanClawback,
2573 mptAlice.authorize({.account = bob});
2574 mptAlice.authorize({.account = carol});
2575 mptAlice.pay(alice, bob, 200);
2576 mptAlice.pay(alice, carol, 100);
2578 mptAlice.generateKeyPair(alice);
2579 mptAlice.generateKeyPair(bob);
2580 mptAlice.generateKeyPair(carol);
2581 mptAlice.set({.issuerPubKey = mptAlice.getPubKey(alice)});
2587 {
"ConfidentialMPTMergeInbox",
"ConfidentialMPTSend",
"ConfidentialMPTConvertBack"}));
2589 env(delegate::set(alice, dave, {
"ConfidentialMPTClawback"}));
2593 auto ticketSeq = env.seq(bob) + 1;
2594 env(ticket::create(bob, 1));
2595 BEAST_EXPECT(env.seq(bob) != ticketSeq);
2599 .holderPubKey = mptAlice.getPubKey(bob),
2600 .ticketSeq = ticketSeq,
2602 env.require(MptBalance(mptAlice, bob, 100));
2605 ticketSeq = env.seq(bob) + 1;
2606 env(ticket::create(bob, 1));
2607 BEAST_EXPECT(env.seq(bob) != ticketSeq);
2608 mptAlice.mergeInbox({.account = bob, .delegate = dave, .ticketSeq = ticketSeq});
2614 .holderPubKey = mptAlice.getPubKey(carol),
2616 mptAlice.mergeInbox({.account = carol});
2619 ticketSeq = env.seq(bob) + 1;
2620 env(ticket::create(bob, 1));
2621 BEAST_EXPECT(env.seq(bob) != ticketSeq);
2627 .ticketSeq = ticketSeq,
2631 ticketSeq = env.seq(bob) + 1;
2632 env(ticket::create(bob, 1));
2633 BEAST_EXPECT(env.seq(bob) != ticketSeq);
2634 mptAlice.convertBack({
2638 .ticketSeq = ticketSeq,
2642 ticketSeq = env.seq(alice) + 1;
2643 env(ticket::create(alice, 1));
2644 BEAST_EXPECT(env.seq(alice) != ticketSeq);
2645 mptAlice.confidentialClaw({
2649 .ticketSeq = ticketSeq,