1#include <test/jtx/Account.h>
2#include <test/jtx/ConfidentialTransfer.h>
3#include <test/jtx/Env.h>
4#include <test/jtx/amount.h>
5#include <test/jtx/flags.h>
6#include <test/jtx/mpt.h>
7#include <test/jtx/pay.h>
8#include <test/jtx/ter.h>
9#include <test/jtx/vault.h>
11#include <xrpl/basics/Buffer.h>
12#include <xrpl/basics/Slice.h>
13#include <xrpl/basics/base_uint.h>
14#include <xrpl/basics/contract.h>
15#include <xrpl/basics/strHex.h>
16#include <xrpl/beast/unit_test/suite.h>
17#include <xrpl/beast/utility/Journal.h>
18#include <xrpl/core/ServiceRegistry.h>
19#include <xrpl/json/json_value.h>
20#include <xrpl/ledger/ApplyView.h>
21#include <xrpl/ledger/OpenView.h>
22#include <xrpl/protocol/AccountID.h>
23#include <xrpl/protocol/ConfidentialTransfer.h>
24#include <xrpl/protocol/Feature.h>
25#include <xrpl/protocol/Indexes.h>
26#include <xrpl/protocol/LedgerFormats.h>
27#include <xrpl/protocol/Protocol.h>
28#include <xrpl/protocol/SField.h>
29#include <xrpl/protocol/STObject.h>
30#include <xrpl/protocol/Serializer.h>
31#include <xrpl/protocol/TER.h>
32#include <xrpl/protocol/TxFlags.h>
33#include <xrpl/protocol/UintTypes.h>
34#include <xrpl/protocol/jss.h>
35#include <xrpl/tx/apply.h>
37#include <openssl/evp.h>
38#include <utility/mpt_utility.h>
41#include <secp256k1_mpt.h>
69 Env env{*
this, features};
72 MPTTester mptAlice(env, alice, {.holders = {bob}});
76 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
82 mptAlice.pay(alice, bob, 100);
84 mptAlice.generateKeyPair(alice);
86 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
88 mptAlice.generateKeyPair(bob);
93 .holderPubKey = mptAlice.getPubKey(bob),
114 Env env{*
this, features};
117 MPTTester mptAlice(env, alice, {.holders = {bob}});
121 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
127 mptAlice.pay(alice, bob, 1);
129 mptAlice.generateKeyPair(alice);
130 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
132 mptAlice.generateKeyPair(bob);
136 .holderPubKey = mptAlice.getPubKey(bob),
149 Env env{*
this, features};
152 MPTTester mptAlice(env, alice, {.holders = {bob}});
156 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
164 mptAlice.generateKeyPair(alice);
165 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
167 mptAlice.generateKeyPair(bob);
173 .holderPubKey = mptAlice.getPubKey(bob),
178 auto const holderCiphertext =
180 auto const issuerCiphertext =
184 jv[jss::Account] = bob.human();
185 jv[jss::TransactionType] = jss::ConfidentialMPTConvert;
186 jv[sfMPTokenIssuanceID] =
to_string(mptAlice.issuanceID());
188 jv[sfHolderEncryptedAmount.jsonName] =
strHex(holderCiphertext);
189 jv[sfIssuerEncryptedAmount.jsonName] =
strHex(issuerCiphertext);
190 jv[sfBlindingFactor.jsonName] =
strHex(blindingFactor);
195 env.require(MptBalance(mptAlice, bob, 0));
205 Env env{*
this, features};
208 Account const auditor(
"auditor");
219 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
225 mptAlice.pay(alice, bob, 100);
227 mptAlice.generateKeyPair(alice);
228 mptAlice.generateKeyPair(auditor);
232 .issuerPubKey = mptAlice.getPubKey(alice),
233 .auditorPubKey = mptAlice.getPubKey(auditor),
236 mptAlice.generateKeyPair(bob);
241 .holderPubKey = mptAlice.getPubKey(bob),
263 Env env{*
this, features};
265 MPTTester mptAlice(env, alice);
268 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
270 mptAlice.generateKeyPair(alice);
275 .holderPubKey = mptAlice.getPubKey(alice),
281 Env env{*
this, features - featureConfidentialTransfer};
284 MPTTester mptAlice(env, alice, {.holders = {bob}});
288 .flags = tfMPTCanTransfer | tfMPTCanLock,
294 mptAlice.pay(alice, bob, 100);
296 mptAlice.generateKeyPair(alice);
297 mptAlice.generateKeyPair(bob);
301 .issuerPubKey = mptAlice.getPubKey(alice),
308 .holderPubKey = mptAlice.getPubKey(bob),
314 Env env{*
this, features};
317 MPTTester mptAlice(env, alice, {.holders = {bob}});
321 .flags = tfMPTCanTransfer | tfMPTCanLock,
327 mptAlice.pay(alice, bob, 100);
329 mptAlice.generateKeyPair(alice);
330 mptAlice.generateKeyPair(bob);
335 .holderPubKey = mptAlice.getPubKey(bob),
343 .holderPubKey = mptAlice.getPubKey(bob),
344 .holderEncryptedAmt =
Buffer{},
352 .holderPubKey = mptAlice.getPubKey(bob),
353 .issuerEncryptedAmt =
Buffer{},
361 .holderPubKey = mptAlice.getPubKey(bob),
362 .auditorEncryptedAmt = gMakeZeroBuffer(10),
370 .holderPubKey = mptAlice.getPubKey(bob),
379 .holderPubKey = mptAlice.getPubKey(bob),
387 .holderPubKey = mptAlice.getPubKey(bob),
397 .holderPubKey = mptAlice.getPubKey(bob),
422 Env env{*
this, features};
425 MPTTester mptAlice(env, alice, {.holders = {bob}});
429 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
435 mptAlice.pay(alice, bob, 100);
437 mptAlice.generateKeyPair(alice);
438 mptAlice.generateKeyPair(bob);
440 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
445 .fillSchnorrProof =
false,
446 .holderPubKey = mptAlice.getPubKey(bob),
453 .fillSchnorrProof =
false,
454 .holderPubKey = mptAlice.getPubKey(bob),
463 .holderPubKey = mptAlice.getPubKey(bob),
471 Env env{*
this, features};
474 MPTTester mptAlice(env, alice, {.holders = {bob}});
478 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
484 mptAlice.pay(alice, bob, 100);
486 mptAlice.generateKeyPair(alice);
487 mptAlice.generateKeyPair(bob);
489 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
496 .holderPubKey = mptAlice.getPubKey(bob),
503 .fillSchnorrProof =
true,
512 testcase(
"Convert proof context binding");
515 auto runBadProof = [&](
auto makeContextHash) {
516 Env env{*
this, features};
520 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
524 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
526 mptAlice.authorize({.account = bob});
527 mptAlice.authorize({.account = carol});
528 mptAlice.pay(alice, bob, 100);
530 mptAlice.generateKeyPair(alice);
531 mptAlice.generateKeyPair(bob);
532 mptAlice.generateKeyPair(carol);
533 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
536 mptAlice.getSchnorrProof(bob, makeContextHash(env, mptAlice, alice, bob, carol));
537 if (!BEAST_EXPECT(proof.has_value()))
544 .holderPubKey = mptAlice.getPubKey(bob),
550 runBadProof([&](Env& env,
551 MPTTester
const& mpt,
559 runBadProof([&](Env& env,
565 bob.id(),
makeMptID(env.seq(alice) + 100, alice), env.seq(bob));
569 runBadProof([&](Env& env,
570 MPTTester
const& mpt,
586 Env env{*
this, features};
588 Account const auditor(
"auditor");
589 MPTTester mptAlice(env, alice, {.holders = {}, .auditor = auditor});
593 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
596 mptAlice.generateKeyPair(alice);
597 mptAlice.generateKeyPair(auditor);
601 .issuerPubKey = mptAlice.getPubKey(alice),
602 .auditorPubKey = mptAlice.getPubKey(auditor),
608 Env env{*
this, features};
610 MPTTester mptAlice(env, alice, {.holders = {}});
614 .flags = tfMPTCanTransfer | tfMPTCanLock,
619 .flags = tfMPTSetCanHoldConfidentialBalance,
625 Env env{*
this, features};
627 Account const auditor(
"auditor");
628 MPTTester mptAlice(env, alice, {.holders = {}, .auditor = auditor});
632 .flags = tfMPTCanTransfer | tfMPTCanLock,
635 mptAlice.generateKeyPair(alice);
636 mptAlice.generateKeyPair(auditor);
640 .flags = tfMPTSetCanHoldConfidentialBalance,
641 .issuerPubKey = mptAlice.getPubKey(alice),
642 .auditorPubKey = mptAlice.getPubKey(auditor),
646 BEAST_EXPECT(mptAlice.checkFlags(
647 lsfMPTCanTransfer | lsfMPTCanLock | lsfMPTCanHoldConfidentialBalance));
652 BEAST_EXPECT(sle->isFieldPresent(sfIssuerEncryptionKey));
653 BEAST_EXPECT(sle->isFieldPresent(sfAuditorEncryptionKey));
664 Env env{*
this, features - featureConfidentialTransfer};
667 MPTTester mptAlice(env, alice, {.holders = {bob}});
671 .flags = tfMPTCanTransfer | tfMPTCanLock,
677 mptAlice.pay(alice, bob, 100);
679 mptAlice.generateKeyPair(alice);
680 mptAlice.generateKeyPair(bob);
684 .issuerPubKey = mptAlice.getPubKey(alice),
691 Env env{*
this, features};
694 MPTTester mptAlice(env, alice, {.holders = {bob}});
698 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
704 mptAlice.pay(alice, bob, 100);
706 mptAlice.generateKeyPair(alice);
707 mptAlice.generateKeyPair(bob);
726 .issuerPubKey = mptAlice.getPubKey(alice),
727 .auditorPubKey = gMakeZeroBuffer(10),
734 .issuerPubKey = mptAlice.getPubKey(alice),
746 .issuerPubKey = mptAlice.getPubKey(alice),
754 .auditorPubKey = mptAlice.getPubKey(alice),
768 Env env{*
this, features};
770 MPTTester mptAlice(env, alice, {.holders = {}});
774 .flags = tfMPTCanTransfer | tfMPTCanLock,
777 mptAlice.generateKeyPair(alice);
781 .issuerPubKey = mptAlice.getPubKey(alice),
788 Env env{*
this, features - featureConfidentialMPTKeyRotation};
791 MPTTester mptAlice(env, alice, {.holders = {bob}});
795 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
798 mptAlice.generateKeyPair(alice);
799 mptAlice.generateKeyPair(bob);
804 .issuerPubKey = mptAlice.getPubKey(alice),
810 .issuerPubKey = mptAlice.getPubKey(bob),
820 Env env{*
this, features - featureConfidentialMPTKeyRotation};
823 Account const auditor(
"auditor");
824 MPTTester mptAlice(env, alice, {.holders = {bob}, .auditor = auditor});
828 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
831 mptAlice.generateKeyPair(alice);
832 mptAlice.generateKeyPair(bob);
833 mptAlice.generateKeyPair(auditor);
838 .issuerPubKey = mptAlice.getPubKey(alice),
839 .auditorPubKey = mptAlice.getPubKey(auditor),
845 .issuerPubKey = mptAlice.getPubKey(bob),
846 .auditorPubKey = mptAlice.getPubKey(alice),
853 Env env{*
this, features};
855 Account const auditor(
"auditor");
856 MPTTester mptAlice(env, alice, {.holders = {}, .auditor = auditor});
860 .flags = tfMPTCanTransfer | tfMPTCanLock,
863 mptAlice.generateKeyPair(alice);
864 mptAlice.generateKeyPair(auditor);
868 .issuerPubKey = mptAlice.getPubKey(alice),
869 .auditorPubKey = mptAlice.getPubKey(auditor),
876 Env env{*
this, features};
878 MPTTester mptAlice(env, alice, {.holders = {}});
883 .flags = tfMPTCanTransfer | tfMPTCanLock,
887 mptAlice.generateKeyPair(alice);
893 .flags = tfMPTSetCanHoldConfidentialBalance,
894 .issuerPubKey = mptAlice.getPubKey(alice),
902 Env env{*
this, features - featureConfidentialMPTKeyRotation};
904 Account const auditor(
"auditor");
905 MPTTester mptAlice(env, alice, {.holders = {}, .auditor = auditor});
909 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
912 mptAlice.generateKeyPair(alice);
913 mptAlice.generateKeyPair(auditor);
918 .issuerPubKey = mptAlice.getPubKey(alice),
926 .issuerPubKey = mptAlice.getPubKey(alice),
927 .auditorPubKey = mptAlice.getPubKey(auditor),
942 Env env{*
this, features};
944 MPTTester mptAlice(env, alice, {.holders = {}});
948 .flags = tfMPTCanTransfer | tfMPTCanHoldConfidentialBalance,
955 .flags = tfMPTCanTransfer | tfMPTCanHoldConfidentialBalance,
962 Env env{*
this, features};
964 MPTTester mptAlice(env, alice, {.holders = {}});
966 mptAlice.create({.ownerCount = 1, .flags = tfMPTCanTransfer | tfMPTCanLock});
970 .flags = tfMPTSetCanHoldConfidentialBalance,
979 Env env{*
this, features};
981 MPTTester mptAlice(env, alice, {.holders = {}});
984 {.transferFee = 100, .ownerCount = 1, .flags = tfMPTCanTransfer | tfMPTCanLock});
988 .flags = tfMPTSetCanHoldConfidentialBalance,
996 Env env{*
this, features};
998 MPTTester mptAlice(env, alice, {.holders = {}});
1002 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance});
1026 Env env{*
this, features};
1029 MPTTester mptAlice(env, alice, {.holders = {bob}});
1033 .flags = tfMPTCanTransfer | tfMPTCanLock,
1036 mptAlice.authorize({
1039 mptAlice.pay(alice, bob, 100);
1041 mptAlice.generateKeyPair(alice);
1042 mptAlice.generateKeyPair(bob);
1047 .holderPubKey = mptAlice.getPubKey(bob),
1054 Env env{*
this, features};
1057 MPTTester mptAlice(env, alice, {.holders = {bob}});
1061 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
1064 mptAlice.authorize({
1067 mptAlice.pay(alice, bob, 100);
1069 mptAlice.generateKeyPair(alice);
1070 mptAlice.generateKeyPair(bob);
1075 .holderPubKey = mptAlice.getPubKey(bob),
1082 Env env{*
this, features};
1085 MPTTester mptAlice(env, alice, {.holders = {bob}});
1089 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
1092 mptAlice.authorize({
1095 mptAlice.generateKeyPair(alice);
1097 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
1100 mptAlice.generateKeyPair(bob);
1105 .holderPubKey = mptAlice.getPubKey(bob),
1112 Env env{*
this, features};
1115 MPTTester mptAlice(env, alice, {.holders = {bob}});
1119 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
1122 mptAlice.generateKeyPair(alice);
1123 mptAlice.generateKeyPair(bob);
1125 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
1130 .holderPubKey = mptAlice.getPubKey(bob),
1137 Env env{*
this, features};
1141 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
1145 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
1148 mptAlice.authorize({
1151 mptAlice.pay(alice, bob, 100);
1153 mptAlice.generateKeyPair(alice);
1154 mptAlice.generateKeyPair(bob);
1155 mptAlice.generateKeyPair(carol);
1157 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
1162 .holderPubKey = mptAlice.getPubKey(bob),
1170 .holderPubKey = mptAlice.getPubKey(bob),
1177 Buffer const holderCiphertext = mptAlice.encryptAmount(bob, amount, blindingFactor);
1182 Buffer const wrongIssuerCiphertext =
1183 mptAlice.encryptAmount(carol, amount, blindingFactor);
1188 .holderPubKey = mptAlice.getPubKey(bob),
1189 .holderEncryptedAmt = holderCiphertext,
1190 .issuerEncryptedAmt = wrongIssuerCiphertext,
1191 .blindingFactor = blindingFactor,
1198 Env env{*
this, features};
1201 MPTTester mptAlice(env, alice, {.holders = {bob}});
1205 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
1208 mptAlice.authorize({
1211 mptAlice.pay(alice, bob, 100);
1213 mptAlice.generateKeyPair(alice);
1215 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
1217 mptAlice.generateKeyPair(bob);
1222 .holderPubKey = mptAlice.getPubKey(bob),
1229 Env env{*
this, features};
1232 MPTTester mptAlice(env, alice, {.holders = {bob}});
1236 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
1239 mptAlice.authorize({
1242 mptAlice.pay(alice, bob, 100);
1244 mptAlice.generateKeyPair(alice);
1246 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
1248 mptAlice.generateKeyPair(bob);
1250 mptAlice.convert({.account = bob, .amt = 10, .holderPubKey = mptAlice.getPubKey(bob)});
1256 .holderPubKey = mptAlice.getPubKey(bob),
1263 Env env{*
this, features};
1266 MPTTester mptAlice(env, alice, {.holders = {bob}});
1270 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
1273 mptAlice.authorize({
1276 mptAlice.pay(alice, bob, 100);
1278 mptAlice.generateKeyPair(alice);
1280 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
1288 mptAlice.generateKeyPair(bob);
1293 .holderPubKey = mptAlice.getPubKey(bob),
1300 .flags = tfMPTUnlock,
1306 .holderPubKey = mptAlice.getPubKey(bob),
1312 Env env{*
this, features};
1315 MPTTester mptAlice(env, alice, {.holders = {bob}});
1319 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTRequireAuth |
1320 tfMPTCanHoldConfidentialBalance,
1323 mptAlice.authorize({
1326 mptAlice.authorize({
1330 mptAlice.pay(alice, bob, 100);
1332 mptAlice.generateKeyPair(alice);
1334 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
1336 mptAlice.generateKeyPair(bob);
1339 mptAlice.authorize({
1342 .flags = tfMPTUnauthorize,
1348 .holderPubKey = mptAlice.getPubKey(bob),
1353 mptAlice.authorize({
1361 .holderPubKey = mptAlice.getPubKey(bob),
1367 Env env{*
this, features};
1370 MPTTester mptAlice(env, alice, {.holders = {bob}});
1374 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
1377 mptAlice.authorize({
1380 mptAlice.pay(alice, bob, 100);
1382 mptAlice.generateKeyPair(alice);
1384 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
1393 mptAlice.generateKeyPair(bob);
1399 .holderPubKey = mptAlice.getPubKey(bob),
1406 Env env{*
this, features};
1409 MPTTester mptAlice(env, alice, {.holders = {bob}});
1413 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTRequireAuth |
1414 tfMPTCanHoldConfidentialBalance,
1417 mptAlice.authorize({
1420 mptAlice.authorize({
1424 mptAlice.pay(alice, bob, 100);
1426 mptAlice.generateKeyPair(alice);
1428 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
1430 mptAlice.generateKeyPair(bob);
1433 mptAlice.authorize({
1436 .flags = tfMPTUnauthorize,
1443 .holderPubKey = mptAlice.getPubKey(bob),
1451 Env env{*
this, features};
1454 Account const auditor(
"auditor");
1465 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
1468 mptAlice.authorize({
1471 mptAlice.pay(alice, bob, 100);
1473 mptAlice.generateKeyPair(alice);
1474 mptAlice.generateKeyPair(bob);
1475 mptAlice.generateKeyPair(auditor);
1479 .issuerPubKey = mptAlice.getPubKey(alice),
1480 .auditorPubKey = mptAlice.getPubKey(auditor)});
1486 .fillAuditorEncryptedAmt =
false,
1487 .holderPubKey = mptAlice.getPubKey(bob),
1495 Env env{*
this, features};
1498 MPTTester mptAlice(env, alice, {.holders = {bob}});
1502 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
1505 mptAlice.authorize({
1508 mptAlice.pay(alice, bob, 100);
1510 mptAlice.generateKeyPair(alice);
1511 mptAlice.generateKeyPair(bob);
1514 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
1519 .holderPubKey = mptAlice.getPubKey(bob),
1528 Env env{*
this, features};
1531 Account const auditor(
"auditor");
1541 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
1544 mptAlice.authorize({
1547 mptAlice.pay(alice, bob, 100);
1549 mptAlice.generateKeyPair(alice);
1550 mptAlice.generateKeyPair(bob);
1551 mptAlice.generateKeyPair(auditor);
1555 .issuerPubKey = mptAlice.getPubKey(alice),
1556 .auditorPubKey = mptAlice.getPubKey(auditor)});
1561 .holderPubKey = mptAlice.getPubKey(bob),
1569 Env env{*
this, features};
1572 MPTTester mptAlice(env, alice, {.holders = {bob}});
1576 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
1579 mptAlice.authorize({
1582 mptAlice.pay(alice, bob, 100);
1584 mptAlice.generateKeyPair(alice);
1585 mptAlice.generateKeyPair(bob);
1587 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
1593 .holderPubKey = mptAlice.getPubKey(bob),
1600 Env env{*
this, features};
1603 MPTTester mptAlice(env, alice, {.holders = {bob}});
1607 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
1610 mptAlice.authorize({
1613 mptAlice.pay(alice, bob, 100);
1615 mptAlice.generateKeyPair(alice);
1616 mptAlice.generateKeyPair(bob);
1618 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
1630 Env env{*
this, features};
1633 MPTTester mptAlice(env, alice, {.holders = {bob}});
1637 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
1640 mptAlice.authorize({
1643 mptAlice.pay(alice, bob, 100);
1645 mptAlice.generateKeyPair(alice);
1647 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
1649 mptAlice.generateKeyPair(bob);
1655 .holderPubKey = mptAlice.getPubKey(bob),
1658 env.require(MptBalance(mptAlice, bob, 0));
1677 Env env{*
this, features};
1680 MPTTester mptAlice(env, alice, {.holders = {bob}});
1684 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
1686 mptAlice.authorize({.account = bob});
1687 mptAlice.pay(alice, bob, 100);
1689 mptAlice.generateKeyPair(alice);
1690 mptAlice.generateKeyPair(bob);
1693 .issuerPubKey = mptAlice.getPubKey(alice),
1699 .holderPubKey = mptAlice.getPubKey(bob),
1702 mptAlice.mergeInbox({.account = bob});
1704 mptAlice.mergeInbox({.account = bob});
1710 Env env{*
this, features};
1713 MPTTester mptAlice(env, alice, {.holders = {bob}});
1717 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
1719 mptAlice.authorize({.account = bob});
1720 mptAlice.pay(alice, bob, 100);
1722 mptAlice.generateKeyPair(alice);
1723 mptAlice.generateKeyPair(bob);
1726 .issuerPubKey = mptAlice.getPubKey(alice),
1732 .holderPubKey = mptAlice.getPubKey(bob),
1738 auto const jt = env.jt(mptAlice.mergeInboxJV({.account = bob}));
1740 auto const sle = std::const_pointer_cast<SLE>(
1741 view.read(keylet::mptoken(mptAlice.issuanceID(), bob.id())));
1745 (*sle)[sfConfidentialBalanceVersion] = wrappedFrom;
1746 view.rawReplace(sle);
1748 auto const result = xrpl::apply(env.app(), view, *jt.stx, TapNone, env.journal);
1749 BEAST_EXPECT(result.ter == tesSUCCESS);
1750 return result.applied;
1753 BEAST_EXPECT(mptAlice.getMPTokenVersion(bob) == 0);
1762 Env env{*
this, features};
1765 MPTTester mptAlice(env, alice, {.holders = {bob}});
1769 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
1772 mptAlice.authorize({
1775 mptAlice.pay(alice, bob, 100);
1777 mptAlice.generateKeyPair(alice);
1779 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
1781 mptAlice.generateKeyPair(bob);
1786 .holderPubKey = mptAlice.getPubKey(bob),
1789 mptAlice.mergeInbox({
1794 env.disableFeature(featureConfidentialTransfer);
1797 mptAlice.mergeInbox({
1811 Env env{*
this, features};
1814 MPTTester mptAlice(env, alice, {.holders = {bob}});
1818 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
1821 mptAlice.authorize({
1824 mptAlice.generateKeyPair(alice);
1826 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
1829 mptAlice.generateKeyPair(bob);
1831 mptAlice.mergeInbox({
1839 Env env{*
this, features};
1842 MPTTester mptAlice(env, alice, {.holders = {bob}});
1846 .flags = tfMPTCanTransfer | tfMPTCanLock,
1849 mptAlice.authorize({
1852 mptAlice.pay(alice, bob, 100);
1854 mptAlice.generateKeyPair(alice);
1855 mptAlice.generateKeyPair(bob);
1857 mptAlice.mergeInbox({
1865 Env env{*
this, features};
1868 MPTTester mptAlice(env, alice, {.holders = {bob}});
1872 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
1875 mptAlice.generateKeyPair(alice);
1877 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
1879 mptAlice.mergeInbox({
1887 Env env{*
this, features};
1890 MPTTester mptAlice(env, alice, {.holders = {bob}});
1894 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
1897 mptAlice.authorize({
1900 mptAlice.pay(alice, bob, 100);
1902 mptAlice.generateKeyPair(alice);
1904 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
1906 mptAlice.generateKeyPair(bob);
1908 mptAlice.mergeInbox({
1916 Env env{*
this, features};
1919 MPTTester mptAlice(env, alice, {.holders = {bob}});
1923 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
1926 mptAlice.authorize({
1929 mptAlice.pay(alice, bob, 100);
1931 mptAlice.generateKeyPair(alice);
1932 mptAlice.generateKeyPair(bob);
1934 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
1939 .holderPubKey = mptAlice.getPubKey(bob),
1949 mptAlice.mergeInbox({
1958 .flags = tfMPTUnlock,
1962 mptAlice.mergeInbox({
1969 Env env{*
this, features};
1972 MPTTester mptAlice(env, alice, {.holders = {bob}});
1976 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance |
1980 mptAlice.authorize({
1983 mptAlice.authorize({
1987 mptAlice.pay(alice, bob, 100);
1989 mptAlice.generateKeyPair(alice);
1990 mptAlice.generateKeyPair(bob);
1992 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
1997 .holderPubKey = mptAlice.getPubKey(bob),
2001 mptAlice.authorize({
2004 .flags = tfMPTUnauthorize,
2007 mptAlice.mergeInbox({
2013 mptAlice.authorize({
2019 mptAlice.mergeInbox({
2028 testcase(
"test confidential send");
2030 Env env{*
this, features};
2031 Account const alice(
"alice"), bob(
"bob"), carol(
"carol");
2035 {{.account = bob, .payAmount = 100, .convertAmount = 60},
2036 {.account = carol, .payAmount = 50, .convertAmount = 20}}};
2037 auto& mptAlice = confEnv.
mpt;
2053 mptAlice.mergeInbox({
2068 testcase(
"test confidential send with auditor");
2070 Env env{*
this, features};
2074 Account const auditor(
"auditor");
2078 {{.account = bob, .payAmount = 100, .convertAmount = 60},
2079 {.account = carol, .payAmount = 50, .convertAmount = 20}},
2080 tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
2082 auto& mptAlice = confEnv.
mpt;
2098 mptAlice.mergeInbox({
2113 testcase(
"test ConfidentialMPTSend Preflight");
2118 Env env{*
this, features - featureConfidentialTransfer};
2122 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
2125 mptAlice.authorize({
2128 mptAlice.authorize({
2145 Env env{*
this, features};
2149 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
2153 .flags = tfMPTCanTransfer | tfMPTCanHoldConfidentialBalance,
2156 mptAlice.authorize({
2159 mptAlice.authorize({
2162 mptAlice.generateKeyPair(alice);
2163 mptAlice.generateKeyPair(bob);
2164 mptAlice.generateKeyPair(carol);
2165 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
2166 mptAlice.pay(alice, bob, 100);
2167 mptAlice.pay(alice, carol, 50);
2172 .holderPubKey = mptAlice.getPubKey(bob),
2178 .holderPubKey = mptAlice.getPubKey(carol),
2211 .senderEncryptedAmt = gMakeZeroBuffer(10),
2220 .destEncryptedAmt = gMakeZeroBuffer(10),
2229 .issuerEncryptedAmt = gMakeZeroBuffer(10),
2286 .amountCommitment = gMakeZeroBuffer(100),
2298 .balanceCommitment = gMakeZeroBuffer(100),
2327 Env env{*
this, features};
2331 Account const auditor(
"auditor");
2336 .holders = {bob, carol},
2342 .flags = tfMPTCanTransfer | tfMPTCanHoldConfidentialBalance,
2345 mptAlice.authorize({
2348 mptAlice.authorize({
2351 mptAlice.generateKeyPair(alice);
2352 mptAlice.generateKeyPair(bob);
2353 mptAlice.generateKeyPair(carol);
2354 mptAlice.generateKeyPair(auditor);
2358 .issuerPubKey = mptAlice.getPubKey(alice),
2359 .auditorPubKey = mptAlice.getPubKey(auditor)});
2360 mptAlice.pay(alice, bob, 100);
2361 mptAlice.pay(alice, carol, 50);
2366 .holderPubKey = mptAlice.getPubKey(bob),
2372 .holderPubKey = mptAlice.getPubKey(carol),
2381 .auditorEncryptedAmt = gMakeZeroBuffer(10),
2404 testcase(
"test ConfidentialMPTSend Preclaim");
2407 Env env{*
this, features};
2413 MPTTester mptAlice(env, alice, {.holders = {bob, carol, dave, eve}});
2417 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTRequireAuth |
2418 tfMPTCanHoldConfidentialBalance,
2420 mptAlice.authorize({
2423 mptAlice.authorize({
2427 mptAlice.authorize({
2430 mptAlice.authorize({
2434 mptAlice.authorize({
2437 mptAlice.authorize({
2443 mptAlice.pay(alice, bob, 100);
2444 mptAlice.pay(alice, carol, 50);
2446 mptAlice.generateKeyPair(alice);
2447 mptAlice.generateKeyPair(bob);
2448 mptAlice.generateKeyPair(carol);
2449 mptAlice.generateKeyPair(dave);
2450 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
2456 .holderPubKey = mptAlice.getPubKey(bob),
2462 .holderPubKey = mptAlice.getPubKey(carol),
2467 mptAlice.mergeInbox({
2470 mptAlice.mergeInbox({
2476 Env env{*
this, features};
2480 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
2483 .flags = tfMPTCanTransfer | tfMPTCanHoldConfidentialBalance,
2485 mptAlice.authorize({
2488 mptAlice.authorize({
2491 mptAlice.generateKeyPair(alice);
2492 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
2498 jv[jss::Account] = bob.human();
2499 jv[jss::Destination] = carol.human();
2500 jv[jss::TransactionType] = jss::ConfidentialMPTSend;
2501 jv[sfMPTokenIssuanceID] =
to_string(mptAlice.issuanceID());
2514 Account const unknown(
"unknown");
2531 env(fset(carol, asfRequireDest));
2547 env(fclear(carol, asfRequireDest));
2611 .flags = tfMPTUnlock,
2639 .flags = tfMPTUnlock,
2667 .flags = tfMPTUnlock,
2680 mptAlice.authorize({
2683 .flags = tfMPTUnauthorize,
2692 mptAlice.authorize({
2707 mptAlice.authorize({
2710 .flags = tfMPTUnauthorize,
2719 mptAlice.authorize({
2733 Env env{*
this, features};
2740 {{.account = bob, .payAmount = 100, .convertAmount = 60},
2741 {.account = carol, .payAmount = 50, .convertAmount = 20}},
2742 tfMPTCanLock | tfMPTCanHoldConfidentialBalance};
2743 auto& mptAlice = confEnv.
mpt;
2757 Env env{*
this, features};
2764 {{.account = bob, .payAmount = 100, .convertAmount = 60},
2765 {.account = carol, .payAmount = 50, .convertAmount = 20}}};
2766 auto& mptAlice = confEnv.
mpt;
2769 auto const issuance = std::const_pointer_cast<SLE>(
2770 view.read(keylet::mptokenIssuance(mptAlice.issuanceID())));
2774 issuance->setFieldU16(sfTransferFee, 1);
2775 view.rawReplace(issuance);
2790 Env env{*
this, features};
2797 {{.account = bob, .payAmount = 100, .convertAmount = 60},
2798 {.account = carol, .payAmount = 50, .convertAmount = 20}}};
2799 auto& mptAlice = confEnv.
mpt;
2824 Env env{*
this, features};
2828 Account const auditor(
"auditor");
2833 .holders = {bob, carol},
2839 .flags = tfMPTCanTransfer | tfMPTCanHoldConfidentialBalance,
2842 mptAlice.authorize({
2845 mptAlice.authorize({
2848 mptAlice.generateKeyPair(alice);
2849 mptAlice.generateKeyPair(bob);
2850 mptAlice.generateKeyPair(carol);
2851 mptAlice.generateKeyPair(auditor);
2855 .issuerPubKey = mptAlice.getPubKey(alice),
2856 .auditorPubKey = mptAlice.getPubKey(auditor)});
2857 mptAlice.pay(alice, bob, 100);
2858 mptAlice.pay(alice, carol, 50);
2863 .holderPubKey = mptAlice.getPubKey(bob),
2869 .holderPubKey = mptAlice.getPubKey(carol),
2888 testcase(
"test ConfidentialMPTSend Range Proof");
2891 Env env{*
this, features};
2892 Account const alice(
"alice"), bob(
"bob"), carol(
"carol");
2896 {{.account = bob, .payAmount = 1000, .convertAmount = 60},
2897 {.account = carol, .payAmount = 1000, .convertAmount = 50}}};
2898 auto& mptAlice = confEnv.
mpt;
2916 mptAlice.mergeInbox({
2940 Env env2{*
this, features};
2941 Account const alice2(
"alice"), bob2(
"bob"), carol2(
"carol");
2945 {{.account = bob2, .payAmount = 100, .convertAmount = 0},
2946 {.account = carol2, .payAmount = 50, .convertAmount = 0}}};
2947 auto& mptAlice2 = zeroEnv.
mpt;
2956 mptAlice2.getDecryptedBalance(bob2, MPTTester::holderEncryptedSpending) == 0);
2970 testcase(
"Send: zero amount — equality and range proof verifier behavior");
2973 Env env{*
this, features};
2977 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
2981 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
2983 mptAlice.authorize({.account = bob});
2984 mptAlice.authorize({.account = carol});
2985 mptAlice.pay(alice, bob, 100);
2986 mptAlice.pay(alice, carol, 50);
2988 mptAlice.generateKeyPair(alice);
2989 mptAlice.generateKeyPair(bob);
2990 mptAlice.generateKeyPair(carol);
2992 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
2994 mptAlice.convert({.account = bob, .amt = 100, .holderPubKey = mptAlice.getPubKey(bob)});
2995 mptAlice.mergeInbox({.account = bob});
2997 mptAlice.convert({.account = carol, .amt = 50, .holderPubKey = mptAlice.getPubKey(carol)});
2998 mptAlice.mergeInbox({.account = carol});
3013 .senderEncryptedAmt = mptAlice.encryptAmount(bob, 0, bf),
3014 .destEncryptedAmt = mptAlice.encryptAmount(carol, 0, bf),
3015 .issuerEncryptedAmt = mptAlice.encryptAmount(alice, 0, bf),
3031 .senderEncryptedAmt = mptAlice.encryptAmount(bob, 0, bf2),
3032 .destEncryptedAmt = mptAlice.encryptAmount(carol, 0, bf2),
3033 .issuerEncryptedAmt = mptAlice.encryptAmount(alice, 0, bf2),
3040 BEAST_EXPECT(mptAlice.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending) == 100);
3041 BEAST_EXPECT(mptAlice.getDecryptedBalance(carol, MPTTester::holderEncryptedInbox) == 0);
3052 Env env{*
this, features};
3055 MPTTester mptAlice(env, alice, {.holders = {bob}});
3059 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
3062 mptAlice.authorize({
3065 mptAlice.pay(alice, bob, 100);
3067 mptAlice.generateKeyPair(alice);
3069 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
3071 mptAlice.generateKeyPair(bob);
3076 .holderPubKey = mptAlice.getPubKey(bob),
3079 mptAlice.authorize({
3081 .flags = tfMPTUnauthorize,
3088 Env env{*
this, features};
3092 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
3096 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
3099 mptAlice.authorize({
3102 mptAlice.authorize({
3105 mptAlice.pay(alice, bob, 100);
3107 mptAlice.generateKeyPair(alice);
3109 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
3111 mptAlice.generateKeyPair(bob);
3112 mptAlice.generateKeyPair(carol);
3117 .holderPubKey = mptAlice.getPubKey(bob),
3123 .holderPubKey = mptAlice.getPubKey(carol),
3127 mptAlice.authorize({
3129 .flags = tfMPTUnauthorize,
3136 Env env{*
this, features};
3139 MPTTester mptAlice(env, alice, {.holders = {bob}});
3143 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
3146 mptAlice.authorize({
3149 mptAlice.generateKeyPair(alice);
3151 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
3153 mptAlice.generateKeyPair(bob);
3158 .holderPubKey = mptAlice.getPubKey(bob),
3161 mptAlice.authorize({
3163 .flags = tfMPTUnauthorize,
3170 Env env{*
this, features};
3173 MPTTester mptAlice(env, alice, {.holders = {bob}});
3177 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
3180 mptAlice.authorize({
3183 mptAlice.generateKeyPair(alice);
3185 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
3187 mptAlice.generateKeyPair(bob);
3192 .holderPubKey = mptAlice.getPubKey(bob),
3197 mptAlice.authorize({
3199 .flags = tfMPTUnauthorize,
3205 Env env{*
this, features};
3209 env, alice, {{.account = bob, .payAmount = 100, .convertAmount = 100}}};
3210 auto& mptAlice = confEnv.
mpt;
3217 mptAlice.pay(bob, alice, 100);
3220 mptAlice.authorize({
3222 .flags = tfMPTUnauthorize,
3229 Env env{*
this, features | featureSingleAssetVault};
3230 Account const issuer(
"issuer");
3232 Account const depositor(
"depositor");
3234 MPTTester mptt{env, issuer, {.holders = {owner, depositor}}};
3236 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanClawback,
3238 PrettyAsset
const asset = mptt.issuanceID();
3239 mptt.authorize({.account = owner});
3240 mptt.authorize({.account = depositor});
3241 env(pay(issuer, depositor, asset(1000)));
3245 auto [tx, vaultKeylet] = vault.create({.owner = owner, .asset = asset});
3250 auto const vaultSle = env.le(vaultKeylet);
3251 BEAST_EXPECT(vaultSle !=
nullptr);
3252 auto const share = vaultSle->at(sfShareMPTID);
3256 {.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)});
3262 BEAST_EXPECT(shareMpt !=
nullptr);
3274 issuance->setFlag(lsfMPTCanHoldConfidentialBalance);
3288 sle->setFieldVL(sfConfidentialBalanceSpending, dummyCiphertext);
3289 sle->setFieldVL(sfConfidentialBalanceInbox, dummyCiphertext);
3290 sle->setFieldVL(sfIssuerEncryptedBalance, dummyCiphertext);
3296 tx = vault.withdraw(
3297 {.depositor = depositor, .id = vaultKeylet.key, .amount = asset(100)});
3303 BEAST_EXPECT(shareMpt !=
nullptr);
3315 Env env{*
this, features};
3319 env, alice, {{.account = bob, .payAmount = 100, .convertAmount = 40}}};
3320 auto& mptAlice = confEnv.
mpt;
3327 mptAlice.convertBack({
3335 Env env{*
this, features};
3339 env, alice, {{.account = bob, .payAmount = 2, .convertAmount = 2}}};
3340 auto& mptAlice = confEnv.
mpt;
3354 Env env{*
this, features};
3357 MPTTester mptAlice(env, alice, {.holders = {bob}});
3361 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
3364 mptAlice.authorize({
3369 mptAlice.generateKeyPair(alice);
3370 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
3372 mptAlice.generateKeyPair(bob);
3376 auto const convertHolderCiphertext =
3378 auto const convertIssuerCiphertext =
3380 auto const convertContextHash =
3383 mptAlice.getSchnorrProof(bob, convertContextHash),
"Missing schnorr proof");
3387 jv[jss::Account] = bob.human();
3388 jv[jss::TransactionType] = jss::ConfidentialMPTConvert;
3389 jv[sfMPTokenIssuanceID] =
to_string(mptAlice.issuanceID());
3391 jv[sfHolderEncryptionKey.jsonName] =
3393 jv[sfHolderEncryptedAmount.jsonName] =
strHex(convertHolderCiphertext);
3394 jv[sfIssuerEncryptedAmount.jsonName] =
strHex(convertIssuerCiphertext);
3395 jv[sfBlindingFactor.jsonName] =
strHex(convertBlindingFactor);
3396 jv[sfZKProof.jsonName] =
strHex(schnorrProof);
3404 jv[jss::Account] = bob.human();
3405 jv[jss::TransactionType] = jss::ConfidentialMPTMergeInbox;
3406 jv[sfMPTokenIssuanceID] =
to_string(mptAlice.issuanceID());
3417 auto const convertBackHolderCiphertext =
3418 mptAlice.encryptAmount(bob, convertBackAmt, convertBackBlindingFactor);
3419 auto const convertBackIssuerCiphertext =
3420 mptAlice.encryptAmount(alice, convertBackAmt, convertBackBlindingFactor);
3424 mptAlice.getEncryptedBalance(bob, MPTTester::holderEncryptedSpending),
3425 "Missing encrypted spending balance");
3429 Buffer const pedersenCommitment =
3433 auto const version = mptAlice.getMPTokenVersion(bob);
3434 UInt256 const convertBackContextHash =
3437 auto const proof = mptAlice.getConvertBackProof(
3440 convertBackContextHash,
3442 .pedersenCommitment = pedersenCommitment,
3444 .encryptedAmt = encryptedSpendingBalance,
3445 .blindingFactor = pcBlindingFactor,
3447 if (!BEAST_EXPECT(proof.has_value()))
3452 jv[jss::Account] = bob.human();
3453 jv[jss::TransactionType] = jss::ConfidentialMPTConvertBack;
3454 jv[sfMPTokenIssuanceID] =
to_string(mptAlice.issuanceID());
3456 jv[sfHolderEncryptedAmount.jsonName] =
strHex(convertBackHolderCiphertext);
3457 jv[sfIssuerEncryptedAmount.jsonName] =
strHex(convertBackIssuerCiphertext);
3458 jv[sfBlindingFactor.jsonName] =
strHex(convertBackBlindingFactor);
3459 jv[sfBalanceCommitment.jsonName] =
strHex(pedersenCommitment);
3466 env.require(MptBalance(mptAlice, bob, convertBackAmt));
3473 testcase(
"Convert back with auditor");
3476 Env env{*
this, features};
3479 Account const auditor(
"auditor");
3483 {{.account = bob, .payAmount = 100, .convertAmount = 40}},
3484 tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
3486 auto& mptAlice = confEnv.
mpt;
3497 testcase(
"Convert back preflight");
3501 Env env{*
this, features - featureConfidentialTransfer};
3504 MPTTester mptAlice(env, alice, {.holders = {bob}});
3508 .flags = tfMPTCanTransfer | tfMPTCanLock,
3511 mptAlice.authorize({
3514 mptAlice.pay(alice, bob, 100);
3516 mptAlice.generateKeyPair(alice);
3517 mptAlice.generateKeyPair(bob);
3519 mptAlice.convertBack({
3527 Env env{*
this, features};
3531 env, alice, {{.account = bob, .payAmount = 100, .convertAmount = 40}}};
3532 auto& mptAlice = confEnv.
mpt;
3540 mptAlice.convertBack({
3546 mptAlice.convertBack({
3553 mptAlice.convertBack({
3560 mptAlice.convertBack({
3563 .holderEncryptedAmt =
Buffer{},
3567 mptAlice.convertBack({
3570 .issuerEncryptedAmt =
Buffer{},
3574 mptAlice.convertBack({
3581 mptAlice.convertBack({
3588 mptAlice.convertBack({
3591 .auditorEncryptedAmt = gMakeZeroBuffer(10),
3595 mptAlice.convertBack({
3603 mptAlice.convertBack({
3610 mptAlice.convertBack({
3613 .proof = gMakeZeroBuffer(100),
3627 Env env{*
this, features};
3630 MPTTester mptAlice(env, alice, {.holders = {bob}});
3634 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
3637 mptAlice.authorize({
3640 mptAlice.generateKeyPair(alice);
3642 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
3645 mptAlice.generateKeyPair(bob);
3647 mptAlice.convertBack({
3656 Env env{*
this, features};
3659 MPTTester mptAlice(env, alice, {.holders = {bob}});
3663 .flags = tfMPTCanTransfer | tfMPTCanLock,
3666 mptAlice.authorize({
3669 mptAlice.pay(alice, bob, 100);
3671 mptAlice.generateKeyPair(alice);
3672 mptAlice.generateKeyPair(bob);
3674 mptAlice.convertBack({
3683 Env env{*
this, features};
3686 MPTTester mptAlice(env, alice, {.holders = {bob}});
3690 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
3693 mptAlice.generateKeyPair(alice);
3694 mptAlice.generateKeyPair(bob);
3696 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
3698 mptAlice.convertBack({
3707 Env env{*
this, features};
3710 MPTTester mptAlice(env, alice, {.holders = {bob}});
3714 .flags = tfMPTCanTransfer | tfMPTCanHoldConfidentialBalance,
3717 mptAlice.authorize({
3721 mptAlice.pay(alice, bob, 100);
3722 mptAlice.generateKeyPair(alice);
3723 mptAlice.generateKeyPair(bob);
3724 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
3727 auto const sleBobMpt = env.le(
keylet::mptoken(mptAlice.issuanceID(), bob.id()));
3728 BEAST_EXPECT(sleBobMpt);
3729 BEAST_EXPECT(!sleBobMpt->isFieldPresent(sfHolderEncryptionKey));
3730 BEAST_EXPECT(!sleBobMpt->isFieldPresent(sfConfidentialBalanceSpending));
3731 BEAST_EXPECT(!sleBobMpt->isFieldPresent(sfIssuerEncryptedBalance));
3733 mptAlice.convertBack({
3742 Env env{*
this, features};
3746 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
3750 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
3753 mptAlice.authorize({
3756 mptAlice.authorize({
3759 mptAlice.pay(alice, bob, 100);
3760 mptAlice.pay(alice, carol, 100);
3762 mptAlice.generateKeyPair(alice);
3764 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
3766 mptAlice.generateKeyPair(bob);
3767 mptAlice.generateKeyPair(carol);
3772 .holderPubKey = mptAlice.getPubKey(bob),
3775 mptAlice.mergeInbox({
3782 .holderPubKey = mptAlice.getPubKey(carol),
3785 mptAlice.convertBack({
3794 Env env{*
this, features};
3797 MPTTester mptAlice(env, alice, {.holders = {bob}});
3801 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTRequireAuth |
3802 tfMPTCanHoldConfidentialBalance,
3805 mptAlice.authorize({
3808 mptAlice.authorize({
3812 mptAlice.pay(alice, bob, 100);
3814 mptAlice.generateKeyPair(alice);
3816 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
3818 mptAlice.generateKeyPair(bob);
3823 .holderPubKey = mptAlice.getPubKey(bob),
3826 mptAlice.mergeInbox({
3836 mptAlice.convertBack({
3845 .flags = tfMPTUnlock,
3848 mptAlice.convertBack({
3853 mptAlice.authorize({
3856 .flags = tfMPTUnauthorize,
3859 mptAlice.convertBack({
3865 mptAlice.authorize({
3870 mptAlice.convertBack({
3878 Env env{*
this, features};
3882 env, alice, {{.account = bob, .payAmount = 100, .convertAmount = 50}}};
3883 auto& mptAlice = confEnv.
mpt;
3896 mptAlice.convertBack({
3907 Env env{*
this, features};
3911 env, alice, {{.account = bob, .payAmount = 100, .convertAmount = 50}}};
3912 auto& mptAlice = confEnv.
mpt;
3925 Env env{*
this, features};
3928 Account const auditor(
"auditor");
3932 {{.account = bob, .payAmount = 100, .convertAmount = 50}},
3933 tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
3935 auto& mptAlice = confEnv.
mpt;
3941 .fillAuditorEncryptedAmt =
false,
3947 mptAlice.convertBack({
3959 testcase(
"test ConfidentialMPTClawback");
3962 Env env{*
this, features};
3967 MPTTester mptAlice(env, alice, {.holders = {bob, carol, dave}});
3970 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanClawback |
3971 tfMPTCanHoldConfidentialBalance,
3973 mptAlice.authorize({
3976 mptAlice.pay(alice, bob, 100);
3977 mptAlice.authorize({
3980 mptAlice.pay(alice, carol, 200);
3981 mptAlice.authorize({
3984 mptAlice.pay(alice, dave, 300);
3986 mptAlice.generateKeyPair(alice);
3987 mptAlice.generateKeyPair(bob);
3988 mptAlice.generateKeyPair(carol);
3989 mptAlice.generateKeyPair(dave);
3990 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
3996 mptAlice.convert({.account = bob, .amt = 60, .holderPubKey = mptAlice.getPubKey(bob)});
3999 mptAlice.mergeInbox({
4009 {.account = carol, .amt = 120, .holderPubKey = mptAlice.getPubKey(carol)});
4012 mptAlice.mergeInbox({
4020 mptAlice.convert({.account = dave, .amt = 200, .holderPubKey = mptAlice.getPubKey(dave)});
4034 auto const preBobPublicBalance = mptAlice.getBalance(bob);
4035 auto const preOutstandingAmount = mptAlice.getIssuanceOutstandingBalance();
4036 auto const preConfidentialOutstandingAmount = mptAlice.getIssuanceConfidentialBalance();
4037 BEAST_EXPECT(!env.le(
keylet::mptoken(mptAlice.issuanceID(), alice.id())));
4043 mptAlice.confidentialClaw({
4048 BEAST_EXPECT(mptAlice.getBalance(bob) == preBobPublicBalance);
4049 auto const postOutstandingAmount = mptAlice.getIssuanceOutstandingBalance();
4051 preOutstandingAmount && postOutstandingAmount &&
4052 *postOutstandingAmount == *preOutstandingAmount - 110);
4054 mptAlice.getIssuanceConfidentialBalance() == preConfidentialOutstandingAmount - 110);
4055 BEAST_EXPECT(!env.le(
keylet::mptoken(mptAlice.issuanceID(), alice.id())));
4059 mptAlice.confidentialClaw({
4067 mptAlice.confidentialClaw({
4077 testcase(
"test ConfidentialMPTClawback with auditor");
4080 Env env{*
this, features};
4085 Account const auditor(
"auditor");
4090 .holders = {bob, carol, dave},
4095 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanClawback |
4096 tfMPTCanHoldConfidentialBalance,
4098 mptAlice.authorize({
4101 mptAlice.pay(alice, bob, 100);
4102 mptAlice.authorize({
4105 mptAlice.pay(alice, carol, 200);
4106 mptAlice.authorize({
4109 mptAlice.pay(alice, dave, 300);
4111 mptAlice.generateKeyPair(alice);
4112 mptAlice.generateKeyPair(bob);
4113 mptAlice.generateKeyPair(carol);
4114 mptAlice.generateKeyPair(dave);
4115 mptAlice.generateKeyPair(auditor);
4118 .issuerPubKey = mptAlice.getPubKey(alice),
4119 .auditorPubKey = mptAlice.getPubKey(auditor)});
4125 mptAlice.convert({.account = bob, .amt = 60, .holderPubKey = mptAlice.getPubKey(bob)});
4128 mptAlice.mergeInbox({
4138 {.account = carol, .amt = 120, .holderPubKey = mptAlice.getPubKey(carol)});
4141 mptAlice.mergeInbox({
4149 mptAlice.convert({.account = dave, .amt = 200, .holderPubKey = mptAlice.getPubKey(dave)});
4165 mptAlice.confidentialClaw({
4173 mptAlice.confidentialClaw({
4181 mptAlice.confidentialClaw({
4191 testcase(
"ConfidentialMPTClawback context binding");
4194 auto runBadProof = [&](
auto makeContextHash) {
4195 Env env{*
this, features};
4202 {{.account = bob, .payAmount = 100, .convertAmount = 60}},
4203 tfMPTCanTransfer | tfMPTCanLock | tfMPTCanClawback |
4204 tfMPTCanHoldConfidentialBalance};
4205 auto& mptAlice = confEnv.
mpt;
4207 auto const privKey = mptAlice.
getPrivKey(alice);
4208 if (!BEAST_EXPECT(privKey.has_value()))
4211 auto const proof = mptAlice.getClawbackProof(
4215 makeContextHash(env, mptAlice, alice, bob, carol));
4216 if (!BEAST_EXPECT(proof.has_value()))
4219 mptAlice.confidentialClaw({
4229 runBadProof([&](Env& env,
4230 MPTTester
const& mpt,
4238 runBadProof([&](Env& env,
4244 alice.id(),
makeMptID(env.seq(alice) + 100, alice), env.seq(alice), bob.id());
4248 runBadProof([&](Env& env,
4249 MPTTester
const& mpt,
4254 alice.id(), mpt.issuanceID(), env.seq(alice) + 1, bob.id());
4258 runBadProof([&](Env& env,
4259 MPTTester
const& mpt,
4277 testcase(
"test ConfidentialMPTClawback Preflight");
4282 Env env{*
this, features - featureConfidentialTransfer};
4285 MPTTester mptAlice(env, alice, {.holders = {bob}});
4288 mptAlice.authorize({
4292 mptAlice.confidentialClaw({
4304 Env env{*
this, features};
4308 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
4312 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
4315 mptAlice.authorize({
4318 mptAlice.authorize({
4321 mptAlice.generateKeyPair(alice);
4322 mptAlice.generateKeyPair(bob);
4323 mptAlice.generateKeyPair(carol);
4324 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
4325 mptAlice.pay(alice, bob, 100);
4326 mptAlice.pay(alice, carol, 50);
4329 mptAlice.confidentialClaw({
4339 jv[jss::Account] = alice.human();
4340 jv[sfHolder] = bob.human();
4341 jv[jss::TransactionType] = jss::ConfidentialMPTClawback;
4343 jv[sfZKProof] =
"123";
4346 jv[sfMPTokenIssuanceID] =
"00000004AE123A8556F3CF91154711376AFB0F894F832B3E";
4352 mptAlice.confidentialClaw({
4360 mptAlice.confidentialClaw({
4368 mptAlice.confidentialClaw({
4381 testcase(
"Clawback Preclaim Errors");
4388 Env env{*
this, features};
4393 MPTTester mptAlice(env, alice, {.holders = {bob, carol, dave}});
4396 .flags = tfMPTCanTransfer | tfMPTCanClawback | tfMPTRequireAuth |
4397 tfMPTCanHoldConfidentialBalance,
4399 mptAlice.authorize({
4402 mptAlice.authorize({
4406 mptAlice.authorize({
4409 mptAlice.authorize({
4414 mptAlice.pay(alice, bob, 100);
4415 mptAlice.pay(alice, carol, 50);
4416 mptAlice.generateKeyPair(alice);
4417 mptAlice.generateKeyPair(bob);
4418 mptAlice.generateKeyPair(carol);
4419 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
4424 .holderPubKey = mptAlice.getPubKey(bob),
4426 mptAlice.mergeInbox({
4432 Account const unknown(
"unknown");
4433 mptAlice.confidentialClaw({
4443 mptAlice.confidentialClaw({
4453 mptAlice.confidentialClaw({
4464 Env env{*
this, features};
4467 MPTTester mptAlice(env, alice, {.holders = {bob}});
4470 .flags = tfMPTCanTransfer | tfMPTCanHoldConfidentialBalance,
4472 mptAlice.authorize({
4475 mptAlice.generateKeyPair(alice);
4476 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
4478 mptAlice.confidentialClaw({
4488 Env env{*
this, features};
4491 MPTTester mptAlice(env, alice, {.holders = {bob}});
4493 .flags = tfMPTCanClawback | tfMPTCanHoldConfidentialBalance,
4495 mptAlice.authorize({
4498 mptAlice.generateKeyPair(alice);
4500 mptAlice.confidentialClaw({
4510 Env env{*
this, features};
4513 MPTTester mptAlice(env, alice, {.holders = {bob}});
4515 .flags = tfMPTCanClawback | tfMPTCanHoldConfidentialBalance,
4517 mptAlice.authorize({
4520 mptAlice.generateKeyPair(alice);
4521 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
4527 jv[jss::Account] = alice.human();
4528 jv[sfHolder] = bob.human();
4529 jv[jss::TransactionType] = jss::ConfidentialMPTClawback;
4532 jv[sfZKProof] = dummyProof;
4533 jv[sfMPTokenIssuanceID] =
to_string(mptAlice.issuanceID());
4539 std::uint32_t const setupFlags = tfMPTCanTransfer | tfMPTCanClawback | tfMPTRequireAuth |
4540 tfMPTCanLock | tfMPTCanHoldConfidentialBalance;
4543 auto removeMPTokenField =
4544 [&](Env& env, MPTTester
const& mpt,
Account const& holder,
SField const& field) {
4546 auto const sle = std::const_pointer_cast<SLE>(
4547 view.read(keylet::mptoken(mpt.issuanceID(), holder.id())));
4551 sle->makeFieldAbsent(field);
4552 view.rawReplace(sle);
4561 Env env{*
this, features};
4565 env, alice, {{.account = bob, .payAmount = 100, .convertAmount = 60}}, setupFlags};
4566 auto& mptAlice = confEnv.
mpt;
4574 mptAlice.confidentialClaw({
4578 .proof = dummyClawbackProof,
4586 Env env{*
this, features};
4590 env, alice, {{.account = bob, .payAmount = 100, .convertAmount = 60}}, setupFlags};
4591 auto& mptAlice = confEnv.
mpt;
4593 removeMPTokenField(env, mptAlice, bob, sfIssuerEncryptedBalance);
4594 mptAlice.confidentialClaw({
4598 .proof = dummyClawbackProof,
4605 Env env{*
this, features};
4609 env, alice, {{.account = bob, .payAmount = 100, .convertAmount = 60}}, setupFlags};
4610 auto& mptAlice = confEnv.
mpt;
4612 removeMPTokenField(env, mptAlice, bob, sfHolderEncryptionKey);
4613 mptAlice.confidentialClaw({
4617 .proof = dummyClawbackProof,
4624 Env env{*
this, features};
4628 env, alice, {{.account = bob, .payAmount = 100, .convertAmount = 60}}, setupFlags};
4629 auto& mptAlice = confEnv.
mpt;
4637 mptAlice.confidentialClaw({
4646 Env env{*
this, features};
4650 env, alice, {{.account = bob, .payAmount = 100, .convertAmount = 60}}, setupFlags};
4651 auto& mptAlice = confEnv.
mpt;
4658 mptAlice.confidentialClaw({
4667 Env env{*
this, features};
4671 env, alice, {{.account = bob, .payAmount = 100, .convertAmount = 60}}, setupFlags};
4672 auto& mptAlice = confEnv.
mpt;
4678 .flags = tfMPTUnauthorize,
4681 mptAlice.confidentialClaw({
4691 Env env{*
this, features};
4695 env, alice, {{.account = bob, .payAmount = 100, .convertAmount = 60}}, setupFlags};
4696 auto& mptAlice = confEnv.
mpt;
4710 testcase(
"ConfidentialMPTClawback Proof");
4720 auto setupEnv = [&](Env& env) -> MPTTester {
4721 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
4724 .flags = tfMPTCanTransfer | tfMPTCanClawback | tfMPTCanHoldConfidentialBalance,
4727 for (
auto const& [acct, amt] : {
std::pair{bob, 1000}, {carol, 2000}})
4729 mptAlice.authorize({
4732 mptAlice.pay(alice, acct, amt);
4733 mptAlice.generateKeyPair(acct);
4736 mptAlice.generateKeyPair(alice);
4737 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
4744 auto checkBadProofs =
4746 for (
auto const badAmt : amts)
4748 mpt.confidentialClaw({
4762 Env env{*
this, features};
4763 auto mptAlice = setupEnv(env);
4766 mptAlice.convert({.account = bob, .amt = 500, .holderPubKey = mptAlice.getPubKey(bob)});
4767 mptAlice.mergeInbox({
4772 {.account = carol, .amt = 1000, .holderPubKey = mptAlice.getPubKey(carol)});
4808 mptAlice.confidentialClaw({
4813 mptAlice.confidentialClaw({
4827 Env env{*
this, features};
4828 auto mptAlice = setupEnv(env);
4830 mptAlice.convert({.account = bob, .amt = 300, .holderPubKey = mptAlice.getPubKey(bob)});
4831 mptAlice.mergeInbox({
4835 {.account = carol, .amt = 400, .holderPubKey = mptAlice.getPubKey(carol)});
4836 mptAlice.mergeInbox({
4883 mptAlice.confidentialClaw({
4888 mptAlice.confidentialClaw({
4900 Env env{*
this, features};
4901 auto mptAlice = setupEnv(env);
4903 mptAlice.convert({.account = bob, .amt = 500, .holderPubKey = mptAlice.getPubKey(bob)});
4904 mptAlice.mergeInbox({
4908 auto const privKey = mptAlice.getPrivKey(alice);
4909 if (!BEAST_EXPECT(privKey.has_value()))
4912 auto const proof = mptAlice.getClawbackProof(
4917 alice.id(), mptAlice.issuanceID(), env.seq(alice), bob.id()));
4918 if (!BEAST_EXPECT(proof.has_value()))
4924 auto const versionBefore = mptAlice.getMPTokenVersion(bob);
4925 mptAlice.mergeInbox({.account = bob});
4926 BEAST_EXPECT(mptAlice.getMPTokenVersion(bob) != versionBefore);
4929 mptAlice.confidentialClaw({
4941 testcase(
"Public transfers after clearing Confidential Flag");
4951 auto runPublicPayments = [&](MPTTester& mpt) {
4952 mpt.pay(bob, carol, 10);
4953 mpt.pay(carol, bob, 5);
4954 mpt.pay(alice, bob, 1);
4955 mpt.pay(carol, alice, 5);
4958 auto drainAndDeleteBobMPToken = [&](Env& env, MPTTester& mpt) {
4959 auto const bobBalance = mpt.getBalance(bob);
4960 BEAST_EXPECT(bobBalance > 0);
4962 mpt.pay(bob, alice, bobBalance);
4963 BEAST_EXPECT(mpt.getBalance(bob) == 0);
4965 mpt.authorize({.account = bob, .flags = tfMPTUnauthorize});
4972 Env env{*
this, features};
4976 {{.account = bob, .payAmount = 100, .convertAmount = 50}},
4977 tfMPTCanTransfer | tfMPTCanHoldConfidentialBalance};
4979 env.fund(XRP(1'000), carol);
4981 ct.
mpt.
pay(alice, carol, 50);
4985 runPublicPayments(ct.
mpt);
4986 drainAndDeleteBobMPToken(env, ct.
mpt);
4991 Env env{*
this, features};
4992 Account const auditor(
"auditor");
4993 MPTTester mptAlice(env, alice, {.holders = {bob, carol}, .auditor = auditor});
4997 .flags = tfMPTCanTransfer | tfMPTCanHoldConfidentialBalance,
5000 mptAlice.authorize({.account = bob});
5001 mptAlice.authorize({.account = carol});
5002 mptAlice.pay(alice, bob, 100);
5003 mptAlice.pay(alice, carol, 50);
5005 mptAlice.generateKeyPair(alice);
5006 mptAlice.generateKeyPair(bob);
5007 mptAlice.generateKeyPair(auditor);
5010 .issuerPubKey = mptAlice.getPubKey(alice),
5011 .auditorPubKey = mptAlice.getPubKey(auditor)});
5016 .holderPubKey = mptAlice.getPubKey(bob),
5018 mptAlice.mergeInbox({.account = bob});
5019 mptAlice.convertBack({.account = bob, .amt = 50});
5021 runPublicPayments(mptAlice);
5022 drainAndDeleteBobMPToken(env, mptAlice);
5028 Env env{*
this, features};
5032 {{.account = bob, .payAmount = 100, .convertAmount = 50}},
5033 tfMPTCanTransfer | tfMPTCanClawback | tfMPTCanHoldConfidentialBalance};
5035 env.fund(XRP(1'000), carol);
5037 ct.
mpt.
pay(alice, carol, 50);
5041 runPublicPayments(ct.
mpt);
5042 drainAndDeleteBobMPToken(env, ct.
mpt);
5049 testcase(
"mutate lsfMPTCanHoldConfidentialBalance");
5055 Env env{*
this, features - featureDynamicMPT};
5058 MPTTester mptAlice(env, alice, {.holders = {bob}});
5070 Env env{*
this, features - featureConfidentialTransfer};
5073 MPTTester mptAlice(env, alice, {.holders = {bob}});
5085 Env env{*
this, features};
5088 MPTTester mptAlice(env, alice, {.holders = {bob}});
5092 .flags = tfMPTCanTransfer,
5098 .flags = tfMPTSetCanHoldConfidentialBalance,
5105 Env env{*
this, features};
5108 MPTTester mptAlice(env, alice, {.holders = {bob}});
5112 .flags = tfMPTCanTransfer | tfMPTCanHoldConfidentialBalance,
5116 mptAlice.authorize({
5119 mptAlice.pay(alice, bob, 100);
5121 mptAlice.generateKeyPair(alice);
5122 mptAlice.generateKeyPair(bob);
5123 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
5125 auto holderPubKeySet =
false;
5126 auto verifyToggle = [&](
TER expectedResult, uint64_t amt) {
5127 if (!holderPubKeySet)
5132 .holderPubKey = mptAlice.getPubKey(bob),
5133 .err = expectedResult,
5141 .err = expectedResult,
5147 holderPubKeySet =
true;
5148 mptAlice.mergeInbox({
5154 mptAlice.convertBack({
5165 .flags = tfMPTSetCanHoldConfidentialBalance,
5172 .flags = tfMPTSetCanHoldConfidentialBalance,
5180 Env env{*
this, features};
5183 MPTTester mptAlice(env, alice, {.holders = {bob}});
5189 .flags = tfMPTCanTransfer | tfMPTCanHoldConfidentialBalance,
5192 mptAlice.authorize({
5195 mptAlice.pay(alice, bob, 100);
5197 mptAlice.generateKeyPair(alice);
5198 mptAlice.generateKeyPair(bob);
5199 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
5202 mptAlice.convert({.account = bob, .amt = 50, .holderPubKey = mptAlice.getPubKey(bob)});
5208 .flags = tfMPTSetCanHoldConfidentialBalance,
5217 testcase(
"Convert back pedersen proof");
5220 Env env{*
this, features};
5224 env, alice, {{.account = bob, .payAmount = 100, .convertAmount = 40}}};
5225 auto& mptAlice = confEnv.
mpt;
5229 uint64_t
const amt = 10;
5234 mptAlice.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending),
5235 "Missing spending balance");
5237 mptAlice.getEncryptedBalance(bob, MPTTester::holderEncryptedSpending),
5238 "Missing encrypted spending balance");
5239 BEAST_EXPECT(!encryptedSpendingBalance.empty());
5241 Buffer const pedersenCommitment =
5242 mptAlice.getPedersenCommitment(spendingBalance, pcBlindingFactor);
5243 Buffer const issuerCiphertext = mptAlice.encryptAmount(alice, amt, blindingFactor);
5244 Buffer const bobCiphertext = mptAlice.encryptAmount(bob, amt, blindingFactor);
5245 auto const version = mptAlice.getMPTokenVersion(bob);
5258 Buffer const badPedersenCommitment =
5259 mptAlice.getPedersenCommitment(1, pcBlindingFactor);
5260 auto const proof = mptAlice.getConvertBackProof(
5265 .pedersenCommitment = badPedersenCommitment,
5266 .amt = spendingBalance,
5267 .encryptedAmt = encryptedSpendingBalance,
5268 .blindingFactor = pcBlindingFactor,
5270 if (!BEAST_EXPECT(proof.has_value()))
5273 mptAlice.convertBack({
5277 .holderEncryptedAmt = bobCiphertext,
5278 .issuerEncryptedAmt = issuerCiphertext,
5279 .blindingFactor = blindingFactor,
5280 .pedersenCommitment = pedersenCommitment,
5292 auto const proof = mptAlice.getConvertBackProof(
5297 .pedersenCommitment = pedersenCommitment,
5298 .amt = spendingBalance,
5299 .encryptedAmt = encryptedSpendingBalance,
5302 if (!BEAST_EXPECT(proof.has_value()))
5305 mptAlice.convertBack({
5309 .holderEncryptedAmt = bobCiphertext,
5310 .issuerEncryptedAmt = issuerCiphertext,
5311 .blindingFactor = blindingFactor,
5312 .pedersenCommitment = pedersenCommitment,
5326 uint64_t
constexpr claimedBalance = 20;
5335 encryptedSpendingBalance,
5339 mptAlice.convertBack({
5343 .holderEncryptedAmt = bobCiphertext,
5344 .issuerEncryptedAmt = issuerCiphertext,
5345 .blindingFactor = blindingFactor,
5346 .pedersenCommitment = pedersenCommitment,
5358 Buffer const badPedersenCommitment =
5359 mptAlice.getPedersenCommitment(1, pcBlindingFactor);
5360 auto const proof = mptAlice.getConvertBackProof(
5365 .pedersenCommitment = pedersenCommitment,
5366 .amt = spendingBalance,
5367 .encryptedAmt = encryptedSpendingBalance,
5368 .blindingFactor = pcBlindingFactor,
5370 if (!BEAST_EXPECT(proof.has_value()))
5373 mptAlice.convertBack({
5377 .holderEncryptedAmt = bobCiphertext,
5378 .issuerEncryptedAmt = issuerCiphertext,
5379 .blindingFactor = blindingFactor,
5380 .pedersenCommitment = badPedersenCommitment,
5390 UInt256 const badContextHash{1};
5392 auto const proof = mptAlice.getConvertBackProof(
5397 .pedersenCommitment = pedersenCommitment,
5398 .amt = spendingBalance,
5399 .encryptedAmt = encryptedSpendingBalance,
5400 .blindingFactor = pcBlindingFactor,
5402 if (!BEAST_EXPECT(proof.has_value()))
5405 mptAlice.convertBack({
5409 .holderEncryptedAmt = bobCiphertext,
5410 .issuerEncryptedAmt = issuerCiphertext,
5411 .blindingFactor = blindingFactor,
5412 .pedersenCommitment = pedersenCommitment,
5423 auto const proof = mptAlice.getConvertBackProof(
5428 .pedersenCommitment = pedersenCommitment,
5429 .amt = spendingBalance,
5430 .encryptedAmt = encryptedSpendingBalance,
5431 .blindingFactor = pcBlindingFactor,
5433 if (!BEAST_EXPECT(proof.has_value()))
5436 mptAlice.convertBack({
5440 .holderEncryptedAmt = bobCiphertext,
5441 .issuerEncryptedAmt = issuerCiphertext,
5442 .blindingFactor = blindingFactor,
5443 .pedersenCommitment = pedersenCommitment,
5451 uint64_t
const balance = 100;
5459 testcase(
"Send: overdraft prevention via bulletproof");
5468 Env env{*
this, features};
5469 Account const alice(
"alice"), bob(
"bob"), issuer(
"issuer");
5471 uint64_t
const aliceBalance = balance;
5472 uint64_t
const aliceAmount = amt;
5473 uint64_t
const aliceRemaining = aliceBalance - aliceAmount;
5479 {{.account = alice, .payAmount = 1000, .convertAmount = aliceBalance},
5480 {.account = bob, .payAmount = 1000, .convertAmount = 30}}};
5481 auto& mptIssuer = confEnv.
mpt;
5487 unsigned const numParticipants = 3;
5492 mptIssuer.getDecryptedBalance(alice, MPTTester::holderEncryptedSpending),
5493 "Missing Alice's balance");
5494 BEAST_EXPECT(balance == aliceBalance);
5506 Buffer const aliceEncAmt = mptIssuer.encryptAmount(alice, aliceAmount, randomElgamal);
5507 Buffer const bobEncAmt = mptIssuer.encryptAmount(bob, aliceAmount, randomElgamal);
5508 Buffer const issuerEncAmt = mptIssuer.encryptAmount(issuer, aliceAmount, randomElgamal);
5512 Buffer const amtCommit = mptIssuer.getPedersenCommitment(aliceAmount, randomElgamal);
5513 Buffer const balanceCommit = mptIssuer.getPedersenCommitment(aliceBalance, randomBalance);
5517 mptIssuer.getEncryptedBalance(alice, MPTTester::holderEncryptedSpending),
5518 "Missing Alice's encrypted spending balance");
5520 uint32_t
const version = mptIssuer.getMPTokenVersion(alice);
5522 alice.id(), mptIssuer.issuanceID(), env.seq(alice), bob.id(), version);
5530 auto* ctx = mpt_secp256k1_context();
5531 Buffer sigmaProof(SECP256K1_COMPACT_STANDARD_PROOF_SIZE);
5534 secp256k1_pubkey c1, c2Alice, c2Bob, c2Issuer;
5537 if (!BEAST_EXPECTS(x == 1,
"Failed to parse C1"))
5540 x = secp256k1_ec_pubkey_parse(
5542 auto y = secp256k1_ec_pubkey_parse(
5544 auto z = secp256k1_ec_pubkey_parse(
5549 if (!BEAST_EXPECTS(x == 1 && y == 1 && z == 1,
"Failed to parse C2 components"))
5551 secp256k1_pubkey c2Vec[] = {c2Alice, c2Bob, c2Issuer};
5554 secp256k1_pubkey pkAlice, pkBob, pkIssuer;
5555 auto alicePubKey =
requireOptional(mptIssuer.getPubKey(alice),
"Missing alice pubkey");
5556 auto bobPubKey =
requireOptional(mptIssuer.getPubKey(bob),
"Missing bob pubkey");
5557 auto issuerPubKey =
requireOptional(mptIssuer.getPubKey(issuer),
"Missing issuer pubkey");
5560 z = secp256k1_ec_pubkey_parse(
5562 if (!BEAST_EXPECTS(x == 1 && y == 1 && z == 1,
"Failed to parse public keys"))
5564 secp256k1_pubkey pkVec[] = {pkAlice, pkBob, pkIssuer};
5567 secp256k1_pubkey pcAmount, pcBalance, b1, b2;
5569 y = secp256k1_ec_pubkey_parse(
5571 if (!BEAST_EXPECTS(x == 1 && y == 1,
"Failed to parse commitments"))
5575 y = secp256k1_ec_pubkey_parse(
5577 if (!BEAST_EXPECTS(x == 1 && y == 1,
"Failed to parse balance ciphertext"))
5581 auto alicePrivKey =
requireOptional(mptIssuer.getPrivKey(alice),
"Missing alice privkey");
5585 x = secp256k1_compact_standard_prove(
5590 randomElgamal.
data(),
5591 alicePrivKey.data(),
5592 randomBalance.
data(),
5603 if (!BEAST_EXPECTS(x == 1,
"Failed to generate sigma proof"))
5607 x = secp256k1_compact_standard_verify(
5620 if (!BEAST_EXPECTS(x == 1,
"Sigma verification failed"))
5628 secp256k1_mpt_scalar_negate(negRandomElgamal.
data(), randomElgamal.
data());
5629 secp256k1_mpt_scalar_add(
5630 randomRemaining.
data(), randomBalance.
data(), negRandomElgamal.
data());
5634 {aliceAmount, aliceRemaining}, {randomElgamal, randomRemaining}, ctxHash);
5638 std::memcpy(combinedProof.data(), sigmaProof.
data(), SECP256K1_COMPACT_STANDARD_PROOF_SIZE);
5640 combinedProof.data() + SECP256K1_COMPACT_STANDARD_PROOF_SIZE,
5641 forgedBulletproof.data(),
5645 x = mpt_verify_send_range_proof(
5646 combinedProof.data() + SECP256K1_COMPACT_STANDARD_PROOF_SIZE,
5648 balanceCommit.
data(),
5650 if (!BEAST_EXPECTS(x == errors.
first,
"Forged proof passed validation"))
5659 .proof =
strHex(combinedProof),
5660 .senderEncryptedAmt = aliceEncAmt,
5661 .destEncryptedAmt = bobEncAmt,
5662 .issuerEncryptedAmt = issuerEncAmt,
5663 .amountCommitment = amtCommit,
5664 .balanceCommitment = balanceCommit,
5671 mptIssuer.getDecryptedBalance(alice, MPTTester::holderEncryptedSpending),
5672 "Missing post-attack balance");
5673 if (aliceAmount > aliceBalance)
5675 BEAST_EXPECT(balance == aliceBalance);
5679 BEAST_EXPECT(balance < aliceBalance);
5687 uint64_t
const balance = 100;
5695 testcase(
"Convert back: overdraft prevention via bulletproof");
5704 Env env{*
this, features};
5705 Account const alice(
"alice"), bob(
"bob"), carol(
"carol");
5707 uint64_t
const bobBalance = balance;
5718 {.account = bob, .payAmount = 1000, .convertAmount = bobBalance},
5723 auto& mptAlice = confEnv.
mpt;
5732 mptAlice.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending),
5733 "Missing Bob's balance");
5734 BEAST_EXPECT(balance == bobBalance);
5749 Buffer const balanceCommit = mptAlice.getPedersenCommitment(bobBalance, pcBlindingFactor);
5753 mptAlice.getEncryptedBalance(bob, MPTTester::holderEncryptedSpending),
5754 "Missing Bob's encrypted spending balance");
5756 uint32_t
const version = mptAlice.getMPTokenVersion(bob);
5757 auto const ctxHash =
5761 auto* ctx = mpt_secp256k1_context();
5762 Buffer sigmaProof(SECP256K1_COMPACT_CONVERTBACK_PROOF_SIZE);
5765 secp256k1_pubkey pkBob;
5766 auto bobPubKey =
requireOptional(mptAlice.getPubKey(bob),
"Missing bob pubkey");
5768 if (!BEAST_EXPECTS(x == 1,
"Failed to parse Bob's public key"))
5772 secp256k1_pubkey pcBalance;
5773 x = secp256k1_ec_pubkey_parse(
5775 if (!BEAST_EXPECTS(x == 1,
"Failed to parse balance commitment"))
5779 secp256k1_pubkey b1, b2;
5781 auto y = secp256k1_ec_pubkey_parse(
5783 if (!BEAST_EXPECTS(x == 1 && y == 1,
"Failed to parse balance ciphertext"))
5787 auto bobPrivKey =
requireOptional(mptAlice.getPrivKey(bob),
"Missing bob privkey");
5791 x = secp256k1_compact_convertback_prove(
5796 pcBlindingFactor.
data(),
5802 if (!BEAST_EXPECTS(x == 1,
"Failed to generate convertback sigma proof"))
5806 x = secp256k1_compact_convertback_verify(
5807 ctx, sigmaProof.
data(), &pkBob, &b1, &b2, &pcBalance, ctxHash.data());
5808 if (!BEAST_EXPECTS(x == 1,
"Sigma verification failed"))
5819 auto const forgedBulletproof =
5825 combinedProof.
data(), sigmaProof.
data(), SECP256K1_COMPACT_CONVERTBACK_PROOF_SIZE);
5827 combinedProof.
data() + SECP256K1_COMPACT_CONVERTBACK_PROOF_SIZE,
5828 forgedBulletproof.data(),
5832 x = mpt_verify_convert_back_proof(
5833 combinedProof.
data(),
5835 bobEncBalance.
data(),
5836 balanceCommit.
data(),
5839 if (!BEAST_EXPECTS(x == errors.
first,
"Forged proof verification mismatch"))
5844 mptAlice.convertBack({
5847 .proof = combinedProof,
5848 .holderEncryptedAmt = bobEncAmt,
5849 .issuerEncryptedAmt = issuerEncAmt,
5850 .blindingFactor = blindingFactor,
5851 .pedersenCommitment = balanceCommit,
5858 mptAlice.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending),
5859 "Missing post-attack balance");
5862 BEAST_EXPECT(postBalance == bobBalance);
5866 BEAST_EXPECT(postBalance < bobBalance);
5874 testcase(
"Convert back bulletproof");
5877 Env env{*
this, features};
5881 env, alice, {{.account = bob, .payAmount = 100, .convertAmount = 40}}};
5882 auto& mptAlice = confEnv.
mpt;
5886 uint64_t
const amt = 10;
5891 mptAlice.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending),
5892 "Missing spending balance");
5894 mptAlice.getEncryptedBalance(bob, MPTTester::holderEncryptedSpending),
5895 "Missing encrypted spending balance");
5896 BEAST_EXPECT(!encryptedSpendingBalance.empty());
5898 Buffer const pedersenCommitment =
5899 mptAlice.getPedersenCommitment(spendingBalance, pcBlindingFactor);
5900 Buffer const issuerCiphertext = mptAlice.encryptAmount(alice, amt, blindingFactor);
5901 Buffer const bobCiphertext = mptAlice.encryptAmount(bob, amt, blindingFactor);
5902 auto const version = mptAlice.getMPTokenVersion(bob);
5918 uint64_t
constexpr claimedBalance = 20;
5927 encryptedSpendingBalance,
5931 mptAlice.convertBack({
5935 .holderEncryptedAmt = bobCiphertext,
5936 .issuerEncryptedAmt = issuerCiphertext,
5937 .blindingFactor = blindingFactor,
5938 .pedersenCommitment = pedersenCommitment,
5951 auto const proof = mptAlice.getConvertBackProof(
5956 .pedersenCommitment = pedersenCommitment,
5957 .amt = spendingBalance,
5958 .encryptedAmt = encryptedSpendingBalance,
5961 if (!BEAST_EXPECT(proof.has_value()))
5964 mptAlice.convertBack({
5968 .holderEncryptedAmt = bobCiphertext,
5969 .issuerEncryptedAmt = issuerCiphertext,
5970 .blindingFactor = blindingFactor,
5971 .pedersenCommitment = pedersenCommitment,
5981 UInt256 const badContextHash{1};
5982 auto const proof = mptAlice.getConvertBackProof(
5987 .pedersenCommitment = pedersenCommitment,
5988 .amt = spendingBalance,
5989 .encryptedAmt = encryptedSpendingBalance,
5990 .blindingFactor = pcBlindingFactor,
5992 if (!BEAST_EXPECT(proof.has_value()))
5995 mptAlice.convertBack({
5999 .holderEncryptedAmt = bobCiphertext,
6000 .issuerEncryptedAmt = issuerCiphertext,
6001 .blindingFactor = blindingFactor,
6002 .pedersenCommitment = pedersenCommitment,
6013 auto const proof = mptAlice.getConvertBackProof(
6018 .pedersenCommitment = pedersenCommitment,
6019 .amt = spendingBalance,
6020 .encryptedAmt = encryptedSpendingBalance,
6021 .blindingFactor = pcBlindingFactor,
6023 if (!BEAST_EXPECT(proof.has_value()))
6026 mptAlice.convertBack({
6030 .holderEncryptedAmt = bobCiphertext,
6031 .issuerEncryptedAmt = issuerCiphertext,
6032 .blindingFactor = blindingFactor,
6033 .pedersenCommitment = pedersenCommitment,
6045 testcase(
"ConvertBack proof context binding");
6048 auto runBadProof = [&](
auto makeContextHash) {
6049 Env env{*
this, features};
6054 env, alice, {{.account = bob, .payAmount = 100, .convertAmount = 40}}};
6055 auto& mptAlice = confEnv.
mpt;
6061 auto const spendingBalance =
6062 mptAlice.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending);
6063 auto const encryptedSpendingBalance =
6064 mptAlice.getEncryptedBalance(bob, MPTTester::holderEncryptedSpending);
6065 if (!BEAST_EXPECT(spendingBalance && encryptedSpendingBalance))
6068 Buffer const pedersenCommitment = mptAlice.getPedersenCommitment(
6069 requireOptional(spendingBalance,
"Missing spending balance"), pcBlindingFactor);
6070 Buffer const issuerCiphertext = mptAlice.encryptAmount(alice, amt, blindingFactor);
6071 Buffer const bobCiphertext = mptAlice.encryptAmount(bob, amt, blindingFactor);
6072 auto const version = mptAlice.getMPTokenVersion(bob);
6074 auto const proof = mptAlice.getConvertBackProof(
6077 makeContextHash(env, mptAlice, alice, bob, carol, version),
6079 .pedersenCommitment = pedersenCommitment,
6082 encryptedSpendingBalance,
"Missing encrypted spending balance"),
6083 .blindingFactor = pcBlindingFactor,
6085 if (!BEAST_EXPECT(proof.has_value()))
6088 mptAlice.convertBack({
6092 .holderEncryptedAmt = bobCiphertext,
6093 .issuerEncryptedAmt = issuerCiphertext,
6094 .blindingFactor = blindingFactor,
6095 .pedersenCommitment = pedersenCommitment,
6101 runBadProof([&](Env& env,
6102 MPTTester
const& mpt,
6111 runBadProof([&](Env& env,
6118 bob.id(),
makeMptID(env.seq(alice) + 100, alice), env.seq(bob), version);
6122 runBadProof([&](Env& env,
6123 MPTTester
const& mpt,
6132 runBadProof([&](Env& env,
6133 MPTTester
const& mpt,
6150 testcase(
"ConvertBack: proof ciphertext binding");
6153 Env env{*
this, features};
6154 Account const alice(
"alice"), bob(
"bob");
6156 env, alice, {{.account = bob, .payAmount = 100, .convertAmount = 50}}};
6157 auto& mptAlice = confEnv.
mpt;
6160 mptAlice.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending),
6161 "Missing spending balance");
6163 mptAlice.getEncryptedBalance(bob, MPTTester::holderEncryptedSpending),
6164 "Missing encrypted spending balance");
6165 auto const version = mptAlice.getMPTokenVersion(bob);
6167 Buffer const pedersenCommitment =
6168 mptAlice.getPedersenCommitment(spendingBalance, pcBlindingFactor);
6171 uint64_t
const amtA = 10;
6172 uint32_t
const currentSeq = env.seq(bob);
6176 auto const proofA = mptAlice.getConvertBackProof(
6181 .pedersenCommitment = pedersenCommitment,
6182 .amt = spendingBalance,
6183 .encryptedAmt = encryptedSpendingBalance,
6184 .blindingFactor = pcBlindingFactor,
6186 if (!BEAST_EXPECT(proofA.has_value()))
6190 uint64_t
const amtB = 20;
6192 Buffer const bobCiphertextB = mptAlice.encryptAmount(bob, amtB, blindingFactorB);
6193 Buffer const issuerCiphertextB = mptAlice.encryptAmount(alice, amtB, blindingFactorB);
6196 mptAlice.convertBack({
6200 .holderEncryptedAmt = bobCiphertextB,
6201 .issuerEncryptedAmt = issuerCiphertextB,
6202 .blindingFactor = blindingFactorB,
6203 .pedersenCommitment = pedersenCommitment,
6215 testcase(
"ConvertBack: proof version mismatch");
6218 Env env{*
this, features};
6219 Account const alice(
"alice"), bob(
"bob");
6221 env, alice, {{.account = bob, .payAmount = 1000, .convertAmount = 100}}};
6222 auto& mptAlice = confEnv.
mpt;
6226 mptAlice.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending),
6227 "Missing spending balance");
6229 mptAlice.getEncryptedBalance(bob, MPTTester::holderEncryptedSpending),
6230 "Missing encrypted spending balance");
6233 uint64_t
const amt = 10;
6236 Buffer const pedersenCommitment =
6237 mptAlice.getPedersenCommitment(spendingBalanceV, pcBlindingFactor);
6238 Buffer const issuerCiphertext = mptAlice.encryptAmount(alice, amt, blindingFactor);
6239 Buffer const bobCiphertext = mptAlice.encryptAmount(bob, amt, blindingFactor);
6247 mptAlice.mergeInbox({
6251 BEAST_EXPECT(mptAlice.getMPTokenVersion(bob) > versionV);
6254 uint32_t
const currentSeq = env.seq(bob);
6256 UInt256 const oldContextHash =
6259 auto const oldProof = mptAlice.getConvertBackProof(
6264 .pedersenCommitment = pedersenCommitment,
6265 .amt = spendingBalanceV,
6266 .encryptedAmt = encryptedSpendingBalanceV,
6267 .blindingFactor = pcBlindingFactor,
6269 if (!BEAST_EXPECT(oldProof.has_value()))
6273 mptAlice.convertBack({
6277 .holderEncryptedAmt = bobCiphertext,
6278 .issuerEncryptedAmt = issuerCiphertext,
6279 .blindingFactor = blindingFactor,
6280 .pedersenCommitment = pedersenCommitment,
6293 testcase(
"ConvertBack: homomorphic ciphertext modification");
6296 Env env{*
this, features};
6297 Account const alice(
"alice"), bob(
"bob");
6299 env, alice, {{.account = bob, .payAmount = 100, .convertAmount = 50}}};
6300 auto& mptAlice = confEnv.
mpt;
6303 uint64_t
const amt = 10;
6306 auto const holderCipherText = mptAlice.encryptAmount(bob, amt, bf);
6307 auto const issuerCipherText = mptAlice.encryptAmount(alice, amt, bf);
6312 auto const deltaCipherText = mptAlice.encryptAmount(bob, 1, deltaBf);
6316 homomorphicAdd(holderCipherText, deltaCipherText),
"Missing tampered ciphertext");
6320 mptAlice.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending),
6321 "Missing spending balance");
6323 mptAlice.getEncryptedBalance(bob, MPTTester::holderEncryptedSpending),
6324 "Missing encrypted spending balance");
6326 auto const pedersenCommitment = mptAlice.getPedersenCommitment(spendingBal, pcBf);
6328 auto const currentVersion = mptAlice.getMPTokenVersion(bob);
6333 auto const proof = mptAlice.getConvertBackProof(
6338 .pedersenCommitment = pedersenCommitment,
6340 .encryptedAmt = spendingBalEnc,
6341 .blindingFactor = pcBf,
6343 if (!BEAST_EXPECT(proof.has_value()))
6350 mptAlice.convertBack({
6354 .holderEncryptedAmt = tamperedHolderCipherText,
6355 .issuerEncryptedAmt = issuerCipherText,
6356 .blindingFactor = bf,
6357 .pedersenCommitment = pedersenCommitment,
6372 testcase(
"Send: homomorphic overflow attack via Enc(MAX) + Enc(1)");
6375 Env env{*
this, features};
6376 Account const alice(
"alice"), bob(
"bob"), carol(
"carol");
6380 {{.account = bob, .payAmount = 100, .convertAmount = 100},
6381 {.account = carol, .payAmount = 50, .convertAmount = 50}}};
6382 auto& mptAlice = confEnv.
mpt;
6386 mptAlice.
send({.account = bob, .dest = carol, .amt = 10});
6389 auto const bobSpendingBefore =
6390 mptAlice.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending);
6391 BEAST_EXPECT(bobSpendingBefore == 90);
6399 Buffer const encOne = mptAlice.encryptAmount(bob, 1, bf2);
6413 .senderEncryptedAmt = overflowedCt,
6417 auto const bobSpendingAfter =
6418 mptAlice.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending);
6419 BEAST_EXPECT(bobSpendingBefore == bobSpendingAfter);
6432 testcase(
"ConvertBack: homomorphic underflow attack via Enc(0) - Enc(1)");
6435 Env env{*
this, features};
6436 Account const alice(
"alice"), bob(
"bob");
6438 env, alice, {{.account = bob, .payAmount = 10, .convertAmount = 10}}};
6439 auto& mptAlice = confEnv.
mpt;
6446 auto const bobSpendingBefore =
6447 mptAlice.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending);
6448 BEAST_EXPECT(bobSpendingBefore == 9);
6449 auto const bobPublicBefore = mptAlice.getBalance(bob);
6450 BEAST_EXPECT(bobPublicBefore == 1);
6454 Buffer const encZero = mptAlice.encryptAmount(bob, 0, bf1);
6458 Buffer const encOne = mptAlice.encryptAmount(bob, 1, bf2);
6472 Buffer const pedersenCommitment = mptAlice.getPedersenCommitment(kUnderflowedAmt, pcBf);
6474 auto const currentVersion = mptAlice.getMPTokenVersion(bob);
6478 auto const proof = mptAlice.getConvertBackProof(
6483 .pedersenCommitment = pedersenCommitment,
6484 .amt = kUnderflowedAmt,
6485 .encryptedAmt = underflowedCt,
6486 .blindingFactor = pcBf,
6488 if (!BEAST_EXPECT(proof.has_value()))
6491 mptAlice.convertBack({
6495 .holderEncryptedAmt = underflowedCt,
6496 .pedersenCommitment = pedersenCommitment,
6502 BEAST_EXPECT(mptAlice.getBalance(bob) == bobPublicBefore);
6503 auto const bobSpendingAfter =
6504 mptAlice.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending);
6505 BEAST_EXPECT(bobSpendingBefore == bobSpendingAfter);
6516 testcase(
"Send: off-curve EC points");
6524 Account const alice(
"alice"), bob(
"bob"), carol(
"carol");
6525 Env env{*
this, features};
6529 {{.account = bob, .payAmount = 100, .convertAmount = 60},
6530 {.account = carol, .payAmount = 50, .convertAmount = 30}}};
6531 auto& mptAlice = confEnv.
mpt;
6582 .amountCommitment = badCommitment,
6593 .balanceCommitment = badCommitment,
6604 Account const alice(
"alice"), bob(
"bob"), carol(
"carol");
6605 Env env{*
this, features};
6609 {{.account = bob, .payAmount = 100, .convertAmount = 60},
6610 {.account = carol, .payAmount = 50, .convertAmount = 30}}};
6611 auto& mptAlice = confEnv.
mpt;
6621 .proof =
strHex(badProof),
6631 Account const alice(
"alice"), bob(
"bob"), carol(
"carol");
6632 Env env{*
this, features};
6636 {{.account = bob, .payAmount = 100, .convertAmount = 60},
6637 {.account = carol, .payAmount = 50, .convertAmount = 30}}};
6638 auto& mptAlice = confEnv.
mpt;
6665 .senderEncryptedAmt = badC1goodC2,
6677 .senderEncryptedAmt = goodC1badC2,
6689 .destEncryptedAmt = badC1goodC2,
6701 .destEncryptedAmt = goodC1badC2,
6723 testcase(
"Send: wrong-group point injection rejected");
6726 Env env{*
this, features};
6727 Account const alice(
"alice"), bob(
"bob"), carol(
"carol");
6731 {{.account = bob, .payAmount = 100, .convertAmount = 60},
6732 {.account = carol, .payAmount = 50, .convertAmount = 30}}};
6733 auto& mptAlice = confEnv.
mpt;
6744 0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, 0xBC, 0xE6,
6745 0xE5, 0x63, 0xA4, 0x40, 0xF2, 0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB,
6746 0x33, 0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2, 0x96,
6767 .senderEncryptedAmt = wrongGroupCt,
6779 .destEncryptedAmt = wrongGroupCt,
6791 .issuerEncryptedAmt = wrongGroupCt,
6803 .amountCommitment = wrongGroupCommitment,
6815 .balanceCommitment = wrongGroupCommitment,
6831 testcase(
"Convert: all-zero public key rejected");
6840 Env env{*
this, features};
6841 Account const alice(
"alice"), bob(
"bob"), carol(
"carol");
6842 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
6845 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
6847 mptAlice.authorize({.account = bob});
6848 mptAlice.authorize({.account = carol});
6849 mptAlice.pay(alice, bob, 100);
6850 mptAlice.pay(alice, carol, 50);
6851 mptAlice.generateKeyPair(alice);
6852 mptAlice.generateKeyPair(bob);
6853 mptAlice.generateKeyPair(carol);
6854 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
6860 .holderPubKey = nullKey,
6868 .holderPubKey = nullKey,
6877 Env env{*
this, features};
6878 Account const alice(
"alice"), bob(
"bob");
6879 MPTTester mptAlice(env, alice, {.holders = {bob}});
6882 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
6884 mptAlice.authorize({.account = bob});
6885 mptAlice.pay(alice, bob, 100);
6886 mptAlice.generateKeyPair(alice);
6887 mptAlice.generateKeyPair(bob);
6891 .issuerPubKey = nullKey,
6908 testcase(
"Send: issuer ciphertext encrypted under wrong public key");
6911 Env env{*
this, features};
6912 Account const alice(
"alice"), bob(
"bob"), carol(
"carol");
6916 {{.account = bob, .payAmount = 100, .convertAmount = 100},
6917 {.account = carol, .payAmount = 50, .convertAmount = 50}}};
6918 auto& mptAlice = confEnv.
mpt;
6920 auto const bobSpendingBefore =
6927 Buffer const wrongIssuerCt = mptAlice.encryptAmount(carol, 10, bf);
6933 .issuerEncryptedAmt = wrongIssuerCt,
6942 Buffer const wrongIssuerCt = mptAlice.encryptAmount(bob, 10, bf);
6948 .issuerEncryptedAmt = wrongIssuerCt,
6955 mptAlice.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending) ==
6957 BEAST_EXPECT(mptAlice.getDecryptedBalance(carol, MPTTester::holderEncryptedInbox) == 0);
6965 testcase(
"divergent C1 across participants in ConfidentialMPTSend");
6968 Env env{*
this, features};
6972 Account const auditor(
"auditor");
6976 {{.account = bob, .payAmount = 100, .convertAmount = 50},
6977 {.account = carol, .payAmount = 50, .convertAmount = 50}},
6978 tfMPTCanLock | tfMPTCanHoldConfidentialBalance | tfMPTCanTransfer,
6980 auto& mptAlice = confEnv.
mpt;
6983 uint64_t
const amt = 10;
6985 enum class Participant { Sender, Dest, Issuer, Auditor };
6996 auto const proofOpt =
7010 case Participant::Sender:
7011 senderCt = mptAlice.encryptAmount(bob, amt, bfDivergent);
7013 case Participant::Dest:
7014 destCt = mptAlice.encryptAmount(carol, amt, bfDivergent);
7016 case Participant::Issuer:
7017 issuerCt = mptAlice.encryptAmount(alice, amt, bfDivergent);
7019 case Participant::Auditor:
7020 auditorCt = mptAlice.encryptAmount(auditor, amt, bfDivergent);
7031 .proof =
strHex(proofOpt),
7032 .senderEncryptedAmt = senderCt,
7033 .destEncryptedAmt = destCt,
7034 .issuerEncryptedAmt = issuerCt,
7035 .auditorEncryptedAmt = auditorCt,
7043 auto const spendingAfter =
7044 mptAlice.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending);
7051 BEAST_EXPECT(spendingAfter == setup.
prevSpending - amt);
7057 submitWithDivergentC1(std::nullopt);
7060 submitWithDivergentC1(Participant::Sender);
7061 submitWithDivergentC1(Participant::Dest);
7062 submitWithDivergentC1(Participant::Issuer);
7063 submitWithDivergentC1(Participant::Auditor);
7069 testcase(
"test confidential transactions fee");
7076 .flags = tfMPTCanLock | tfMPTCanHoldConfidentialBalance | tfMPTCanTransfer |
7079 mpt.authorize({.account = bob});
7080 mpt.authorize({.account = carol});
7081 mpt.pay(alice, bob, 100);
7082 mpt.pay(alice, carol, 50);
7083 mpt.generateKeyPair(alice);
7084 mpt.generateKeyPair(bob);
7085 mpt.generateKeyPair(carol);
7086 mpt.set({.account = alice, .issuerPubKey = mpt.getPubKey(alice)});
7091 Env env{*
this, features};
7092 Account const alice(
"alice"), bob(
"bob"), carol(
"carol");
7093 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
7094 setup(mptAlice, alice, bob, carol);
7096 auto const baseFee = env.current()->fees().base;
7101 auto checkFee = [&](
Account const& acct,
auto&& submitFn) {
7102 auto const before = env.balance(acct);
7104 auto const after = env.balance(acct);
7105 BEAST_EXPECT(before -
after == expectedFee);
7108 checkFee(bob, [&]() {
7112 .holderPubKey = mptAlice.getPubKey(bob),
7113 .fee = expectedFee});
7115 checkFee(carol, [&]() {
7119 .holderPubKey = mptAlice.getPubKey(carol),
7120 .fee = expectedFee});
7122 checkFee(bob, [&]() { mptAlice.mergeInbox({.account = bob, .fee = expectedFee}); });
7123 checkFee(carol, [&]() { mptAlice.mergeInbox({.account = carol, .fee = expectedFee}); });
7124 checkFee(bob, [&]() {
7125 mptAlice.send({.account = bob, .dest = carol, .amt = 5, .fee = expectedFee});
7127 checkFee(bob, [&]() {
7128 mptAlice.convertBack({.account = bob, .amt = 5, .fee = expectedFee});
7130 checkFee(alice, [&]() {
7131 mptAlice.confidentialClaw(
7132 {.account = alice, .holder = carol, .amt = 15, .fee = expectedFee});
7136 Account const newIssuerKey(
"newIssuerKey");
7137 mptAlice.generateKeyPair(newIssuerKey);
7138 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(newIssuerKey)});
7139 checkFee(alice, [&]() {
7140 mptAlice.mirrorUpdate(
7144 .fee = expectedFee});
7150 Env env{*
this, features};
7151 Account const alice(
"alice"), bob(
"bob"), carol(
"carol");
7152 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
7153 setup(mptAlice, alice, bob, carol);
7154 auto const baseFee = env.current()->fees().base;
7160 .holderPubKey = mptAlice.getPubKey(bob),
7161 .fee = expectedFee - 1,
7163 mptAlice.mergeInbox({.account = bob, .fee = baseFee, .err =
telINSUF_FEE_P});
7170 mptAlice.convertBack({.account = bob, .amt = 1, .fee = baseFee, .err =
telINSUF_FEE_P});
7171 mptAlice.confidentialClaw(
7177 mptAlice.mirrorUpdate(
7187 Env env{*
this, features};
7188 Account const alice(
"alice"), bob(
"bob"), carol(
"carol");
7189 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
7190 setup(mptAlice, alice, bob, carol);
7192 auto const baseFee = env.current()->fees().base;
7194 auto const bobBefore = env.balance(bob);
7198 .holderPubKey = mptAlice.getPubKey(bob),
7200 BEAST_EXPECT(env.balance(bob) == bobBefore - highFee);
7207 testcase(
"Send: forged equality proof");
7214 Env env{*
this, features};
7215 Account const alice(
"alice"), bob(
"bob"), carol(
"carol");
7219 {{.account = bob}, {.account = carol, .payAmount = 1000, .convertAmount = 50}}};
7220 auto& mptAlice = confEnv.
mpt;
7226 auto const proof = setup.
generateProof(mptAlice, env, bob, carol);
7227 if (!BEAST_EXPECT(proof.has_value()))
7231 auto const forgedDestAmt = mptAlice.encryptAmount(carol, 20, forgedBlindingFactor);
7236 mptAlice.send(args);
7241 auto const proof = setup.
generateProof(mptAlice, env, bob, carol);
7242 if (!BEAST_EXPECT(proof.has_value()))
7246 auto const forgedSenderAmt = mptAlice.encryptAmount(bob, 5, forgedBlindingFactor);
7251 mptAlice.send(args);
7256 auto const proof = setup.
generateProof(mptAlice, env, bob, carol);
7257 if (!BEAST_EXPECT(proof.has_value()))
7261 auto const forgedIssuerAmt = mptAlice.encryptAmount(alice, 100, forgedBlindingFactor);
7266 mptAlice.send(args);
7273 testcase(
"Send: forged range proof");
7281 Env env{*
this, features};
7282 Account const alice(
"alice"), bob(
"bob"), carol(
"carol");
7286 {{.account = bob}, {.account = carol, .payAmount = 1000, .convertAmount = 50}}};
7287 auto& mptAlice = confEnv.
mpt;
7293 auto const senderAmt = mptAlice.encryptAmount(bob, badAmount, blindingFactor);
7294 auto const destAmt = mptAlice.encryptAmount(carol, badAmount, blindingFactor);
7295 auto const issuerAmt = mptAlice.encryptAmount(alice, badAmount, blindingFactor);
7296 auto const amountCommitment = mptAlice.getPedersenCommitment(badAmount, blindingFactor);
7300 mptAlice.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending),
7301 "Missing previous spending balance");
7303 auto const balanceCommitment =
7304 mptAlice.getPedersenCommitment(prevSpending, balanceBlindingFactor);
7309 auto const validProof = setup.
generateProof(mptAlice, env, bob, carol);
7310 if (!BEAST_EXPECT(validProof.has_value()))
7316 forgedProof.
data()[i] ^= 0xFF;
7323 .proof =
strHex(forgedProof),
7324 .senderEncryptedAmt = senderAmt,
7325 .destEncryptedAmt = destAmt,
7326 .issuerEncryptedAmt = issuerAmt,
7327 .amountCommitment = amountCommitment,
7328 .balanceCommitment = balanceCommitment,
7333 mptAlice.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending),
7334 "Missing post spending balance");
7335 BEAST_EXPECT(postSpending == prevSpending);
7341 testcase(
"Send: negative value malleability");
7350 Env env{*
this, features};
7351 Account const alice(
"alice"), bob(
"bob"), carol(
"carol");
7355 {{.account = bob, .payAmount = 1000, .convertAmount = 10},
7356 {.account = carol, .payAmount = 1000, .convertAmount = 50}}};
7357 auto& mptAlice = confEnv.
mpt;
7359 uint64_t
const sendAmount = 10;
7360 auto const negativeRemaining =
static_cast<uint64_t
>(-10);
7365 bob.id(), mptAlice.issuanceID(), env.seq(bob), carol.id(), setup.
version);
7367 auto const validProof = setup.
generateProof(mptAlice, env, bob, carol);
7368 if (!BEAST_EXPECT(validProof.has_value()))
7373 {sendAmount, negativeRemaining},
7384 forgedBulletproof.data(),
7391 mptAlice.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending),
7392 "Missing post spending balance");
7399 testcase(
"Send proof context binding");
7402 auto runBadProof = [&](
auto makeContextHash) {
7403 Env env{*
this, features};
7410 {{.account = bob, .payAmount = 100, .convertAmount = 40}, {.account = carol}}};
7411 auto& mptAlice = confEnv.
mpt;
7415 auto const proof = mptAlice.getConfidentialSendProof(
7420 makeContextHash(env, mptAlice, alice, bob, carol, setup.
version),
7422 .pedersenCommitment = setup.amountCommitment,
7423 .amt = setup.sendAmount,
7424 .encryptedAmt = setup.senderAmt,
7425 .blindingFactor = setup.amountBlindingFactor,
7428 .pedersenCommitment = setup.balanceCommitment,
7429 .amt = setup.prevSpending,
7430 .encryptedAmt = setup.prevEncryptedSpending,
7431 .blindingFactor = setup.balanceBlindingFactor,
7433 if (!BEAST_EXPECT(proof.has_value()))
7441 runBadProof([&](Env& env,
7442 MPTTester
const& mpt,
7448 carol.id(), mpt.issuanceID(), env.seq(bob), carol.id(), version);
7452 runBadProof([&](Env& env,
7467 runBadProof([&](Env& env,
7468 MPTTester
const& mpt,
7474 bob.id(), mpt.issuanceID(), env.seq(bob) + 1, carol.id(), version);
7478 runBadProof([&](Env& env,
7479 MPTTester
const& mpt,
7484 return getSendContextHash(bob.id(), mpt.issuanceID(), env.seq(bob), bob.id(), version);
7488 runBadProof([&](Env& env,
7489 MPTTester
const& mpt,
7495 bob.id(), mpt.issuanceID(), env.seq(bob), carol.id(), version + 1);
7502 testcase(
"Send: Fiat-Shamir Binding");
7505 Env env{*
this, features};
7506 Account const alice(
"alice"), bob(
"bob"), carol(
"carol");
7510 {{.account = bob}, {.account = carol, .payAmount = 1000, .convertAmount = 50}}};
7511 auto& mptAlice = confEnv.
mpt;
7517 auto const proof = setup.
generateProof(mptAlice, env, bob, carol);
7518 if (!BEAST_EXPECT(proof.has_value()))
7522 auto const forgedCommitment =
7523 mptAlice.getPedersenCommitment(setup.
sendAmount + 5, forgedBlindingFactor);
7528 mptAlice.send(args);
7533 auto const proof = setup.
generateProof(mptAlice, env, bob, carol);
7534 if (!BEAST_EXPECT(proof.has_value()))
7537 mptAlice.pay(bob, carol, 1);
7546 auto const proof = setup.
generateProof(mptAlice, env, bob, carol);
7547 if (!BEAST_EXPECT(proof.has_value()))
7551 Buffer tamperedProof(proofRef.size());
7553 size_t const tamperOffset = tamperedProof.
size() / 2;
7554 tamperedProof.
data()[tamperOffset] ^= 0xFF;
7563 testcase(
"Send: Proof Component Reuse");
7566 Env env{*
this, features};
7567 Account const alice(
"alice"), bob(
"bob"), carol(
"carol"), dan(
"dan");
7572 {.account = carol, .payAmount = 1000, .convertAmount = 50},
7573 {.account = dan, .payAmount = 1000, .convertAmount = 50}}};
7574 auto& mptAlice = confEnv.
mpt;
7576 uint64_t
const sendAmount = 10;
7582 auto const proof = setup.
generateProof(mptAlice, env, bob, carol);
7583 if (!BEAST_EXPECT(proof.has_value()))
7587 mptAlice.mergeInbox({.account = carol});
7597 auto const proof = setup.
generateProof(mptAlice, env, bob, carol);
7598 if (!BEAST_EXPECT(proof.has_value()))
7602 mptAlice.mergeInbox({.account = carol});
7604 auto const destAmtDan = mptAlice.encryptAmount(dan, sendAmount, setup.
blindingFactor);
7605 auto const issuerAmtDan =
7611 args.issuerEncryptedAmt = issuerAmtDan;
7612 mptAlice.send(args);
7619 testcase(
"Send: special witness values");
7622 Env env{*
this, features};
7623 Account const alice(
"alice"), bob(
"bob"), carol(
"carol");
7627 {{.account = bob}, {.account = carol, .payAmount = 1000, .convertAmount = 50}}};
7628 auto& mptAlice = confEnv.
mpt;
7634 auto const proof = setup.
generateProof(mptAlice, env, bob, carol);
7635 if (!BEAST_EXPECT(proof.has_value()))
7640 static constexpr size_t kSigmaScalarSize = 32;
7641 static constexpr size_t kChallengeOffset = 0;
7642 static constexpr size_t kResponseOffset = kChallengeOffset + kSigmaScalarSize;
7643 static constexpr size_t kResponseSize = 5 * kSigmaScalarSize;
7651 auto const proof = setup.
generateProof(mptAlice, env, bob, carol);
7652 if (!BEAST_EXPECT(proof.has_value()))
7661 mptAlice.send(args);
7666 auto const proof = setup.
generateProof(mptAlice, env, bob, carol);
7667 if (!BEAST_EXPECT(proof.has_value()))
7676 mptAlice.send(args);
7681 auto const proof = setup.
generateProof(mptAlice, env, bob, carol);
7682 if (!BEAST_EXPECT(proof.has_value()))
7687 static constexpr unsigned char kCurveOrder[32] = {
7688 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
7689 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
7690 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B,
7691 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41
7701 auto const proof = setup.
generateProof(mptAlice, env, bob, carol);
7702 if (!BEAST_EXPECT(proof.has_value()))
7707 static constexpr unsigned char kOverflowScalar[32] = {
7708 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
7709 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
7710 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B,
7711 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x42
7723 testcase(
"Send: cross-statement proof substitution");
7730 Env env{*
this, features};
7731 Account const alice(
"alice"), bob(
"bob"), carol(
"carol");
7735 {{.account = bob}, {.account = carol, .payAmount = 1000, .convertAmount = 50}},
7736 tfMPTCanLock | tfMPTCanHoldConfidentialBalance | tfMPTCanTransfer | tfMPTCanClawback};
7737 auto& mptAlice = confEnv.
mpt;
7739 uint64_t
const sendAmount = 10;
7756 mptAlice.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending),
7757 "Missing spending balance");
7759 mptAlice.getEncryptedBalance(bob, MPTTester::holderEncryptedSpending),
7760 "Missing encrypted spending balance");
7763 Buffer const pedersenCommitment =
7764 mptAlice.getPedersenCommitment(spendingBalance, pcBlindingFactor);
7766 auto const version = mptAlice.getMPTokenVersion(bob);
7767 UInt256 const convertBackCtxHash =
7770 auto const convertBackProof = mptAlice.getConvertBackProof(
7775 .pedersenCommitment = pedersenCommitment,
7776 .amt = spendingBalance,
7777 .encryptedAmt = encryptedSpending,
7778 .blindingFactor = pcBlindingFactor,
7780 if (!BEAST_EXPECT(convertBackProof.has_value()))
7787 Buffer resizedProof(expectedSendSize);
7788 Buffer const& convertBackProofRef =
7790 auto const copyLen =
std::min(convertBackProofRef.
size(), expectedSendSize);
7793 if (copyLen < expectedSendSize)
7794 std::memset(resizedProof.
data() + copyLen, 0, expectedSendSize - copyLen);
7810 UInt192 const fakeIssuanceID{1};
7812 bob.id(), fakeIssuanceID, env.seq(bob), carol.id(), setup.
version);
7815 auto const wrongProof = mptAlice.getConfidentialSendProof(
7822 .pedersenCommitment = setup.amountCommitment,
7824 .encryptedAmt = setup.senderAmt,
7825 .blindingFactor = setup.amountBlindingFactor,
7828 .pedersenCommitment = setup.balanceCommitment,
7829 .amt = setup.prevSpending,
7830 .encryptedAmt = setup.prevEncryptedSpending,
7831 .blindingFactor = setup.balanceBlindingFactor,
7834 if (!BEAST_EXPECT(wrongProof.has_value()))
7848 testcase(
"Send: ciphertext malleability");
7855 Env env{*
this, features};
7856 Account const alice(
"alice"), bob(
"bob"), carol(
"carol");
7860 {{.account = bob}, {.account = carol, .payAmount = 1000, .convertAmount = 50}},
7861 tfMPTCanTransfer | tfMPTCanHoldConfidentialBalance};
7862 auto& mptAlice = confEnv.
mpt;
7864 uint64_t
const sendAmount = 10;
7871 auto const seq = env.seq(bob);
7872 auto jv = mptAlice.sendJV({.account = bob, .dest = carol, .amt = sendAmount}, seq);
7873 auto jtx = env.jt(jv);
7874 BEAST_EXPECT(jtx.stx);
7885 auto const inflatedCiphertext = mptAlice.encryptAmount(carol, sendAmount * 2, bf);
7886 obj.
setFieldVL(sfDestinationEncryptedAmount, inflatedCiphertext);
7893 auto const jr = env.rpc(
"submit",
strHex(tampered.
slice()));
7894 BEAST_EXPECT(jr[jss::result][jss::error] ==
"invalidTransaction");
7906 bob.id(), mptAlice.issuanceID(), env.seq(bob), carol.id(), setup.
version);
7908 auto const validProof = mptAlice.getConfidentialSendProof(
7915 .pedersenCommitment = setup.amountCommitment,
7917 .encryptedAmt = setup.senderAmt,
7918 .blindingFactor = setup.amountBlindingFactor,
7921 .pedersenCommitment = setup.balanceCommitment,
7922 .amt = setup.prevSpending,
7923 .encryptedAmt = setup.prevEncryptedSpending,
7924 .blindingFactor = setup.balanceBlindingFactor,
7927 if (!BEAST_EXPECT(validProof.has_value()))
7933 auto const inflatedDestAmt =
7934 mptAlice.encryptAmount(carol, sendAmount * 2, setup.
blindingFactor);
7939 mptAlice.send(args);
7946 testcase(
"Send: ciphertext negation");
7953 Env env{*
this, features};
7954 Account const alice(
"alice"), bob(
"bob"), carol(
"carol");
7958 {{.account = bob}, {.account = carol, .payAmount = 1000, .convertAmount = 50}},
7959 tfMPTCanTransfer | tfMPTCanHoldConfidentialBalance};
7960 auto& mptAlice = confEnv.
mpt;
7962 uint64_t
const sendAmount = 10;
7968 auto negateCiphertext = [](
Buffer const& ct) ->
Buffer {
7970 neg.
data()[0] ^= 0x01;
7979 auto const seq = env.seq(bob);
7980 auto jv = mptAlice.sendJV({.account = bob, .dest = carol, .amt = sendAmount}, seq);
7981 auto jtx = env.jt(jv);
7982 BEAST_EXPECT(jtx.stx);
7990 auto const origDestAmt = obj.
getFieldVL(sfDestinationEncryptedAmount);
7991 Buffer const origBuf(origDestAmt.data(), origDestAmt.size());
7992 auto const negDestAmt = negateCiphertext(origBuf);
7994 sfDestinationEncryptedAmount,
Slice(negDestAmt.data(), negDestAmt.size()));
7999 auto const jr = env.rpc(
"submit",
strHex(tampered.
slice()));
8000 BEAST_EXPECT(jr[jss::result][jss::error] ==
"invalidTransaction");
8009 auto const validProof = setup.
generateProof(mptAlice, env, bob, carol);
8010 if (!BEAST_EXPECT(validProof.has_value()))
8014 auto const negSenderAmt = negateCiphertext(setup.
senderAmt);
8015 auto const negDestAmt = negateCiphertext(setup.
destAmt);
8016 auto const negIssuerAmt = negateCiphertext(setup.
issuerAmt);
8021 args.destEncryptedAmt = negDestAmt;
8022 args.issuerEncryptedAmt = negIssuerAmt;
8023 mptAlice.send(args);
8034 auto const validProof = setup.
generateProof(mptAlice, env, bob, carol);
8035 if (!BEAST_EXPECT(validProof.has_value()))
8038 auto const negSenderAmt = negateCiphertext(setup.
senderAmt);
8043 mptAlice.send(args);
8050 testcase(
"Send: ciphertext combination");
8057 Env env{*
this, features};
8058 Account const alice(
"alice"), bob(
"bob"), carol(
"carol");
8062 {{.account = bob, .payAmount = 1000, .convertAmount = 200},
8063 {.account = carol, .payAmount = 1000, .convertAmount = 100}},
8064 tfMPTCanTransfer | tfMPTCanHoldConfidentialBalance};
8065 auto& mptAlice = confEnv.
mpt;
8067 uint64_t
const m1 = 10;
8068 uint64_t
const m2 = 5;
8074 auto const seq = env.seq(bob);
8075 auto jv = mptAlice.sendJV({.account = bob, .dest = carol, .amt = m1}, seq);
8076 auto jtx = env.jt(jv);
8077 BEAST_EXPECT(jtx.stx);
8085 auto const origDestCt = obj.
getFieldVL(sfDestinationEncryptedAmount);
8089 auto const encM2 = mptAlice.encryptAmount(carol, m2, bf2);
8092 Slice(origDestCt.data(), origDestCt.size()),
Slice(encM2.data(), encM2.size())),
8093 "Missing combined ciphertext");
8095 obj.
setFieldVL(sfDestinationEncryptedAmount, combined);
8100 auto const jr = env.rpc(
"submit",
strHex(tampered.
slice()));
8101 BEAST_EXPECT(jr[jss::result][jss::error] ==
"invalidTransaction");
8112 auto const validProof = setup.
generateProof(mptAlice, env, bob, carol);
8113 if (!BEAST_EXPECT(validProof.has_value()))
8118 auto const encM2 = mptAlice.encryptAmount(carol, m2, bf2);
8120 BEAST_EXPECT(combinedDest.has_value());
8125 mptAlice.send(args);
8137 auto const proof1 = setup1.
generateProof(mptAlice, env, bob, carol);
8138 if (!BEAST_EXPECT(proof1.has_value()))
8144 auto const proof2 = setup2.
generateProof(mptAlice, env, bob, carol);
8145 if (!BEAST_EXPECT(proof2.has_value()))
8150 BEAST_EXPECT(crossCombined.has_value());
8155 mptAlice.send(args);
8162 testcase(
"Send: ciphertext rerandomization");
8173 Env env{*
this, features};
8174 Account const alice(
"alice"), bob(
"bob"), carol(
"carol");
8178 {{.account = bob}, {.account = carol, .payAmount = 1000, .convertAmount = 50}},
8179 tfMPTCanTransfer | tfMPTCanHoldConfidentialBalance};
8180 auto& mptAlice = confEnv.
mpt;
8182 uint64_t
const sendAmount = 10;
8198 auto const seq = env.seq(bob);
8199 auto jv = mptAlice.sendJV({.account = bob, .dest = carol, .amt = sendAmount}, seq);
8200 auto jtx = env.jt(jv);
8201 BEAST_EXPECT(jtx.stx);
8210 auto const otherCt = mptAlice.encryptAmount(carol, 99, bf2);
8213 auto const origDestAmt = obj.
getFieldVL(sfDestinationEncryptedAmount);
8214 Buffer const origBuf(origDestAmt.data(), origDestAmt.size());
8215 auto const rerandomized = substituteC1(origBuf, otherCt);
8217 sfDestinationEncryptedAmount,
Slice(rerandomized.data(), rerandomized.size()));
8223 auto const jr = env.rpc(
"submit",
strHex(tampered.
slice()));
8224 BEAST_EXPECT(jr[jss::result][jss::error] ==
"invalidTransaction");
8235 auto const validProof = setup.
generateProof(mptAlice, env, bob, carol);
8236 if (!BEAST_EXPECT(validProof.has_value()))
8241 auto const otherCt = mptAlice.encryptAmount(carol, sendAmount, bf2);
8244 auto const rerandomizedDest = substituteC1(setup.
destAmt, otherCt);
8249 mptAlice.send(args);
8256 testcase(
"Send: zero randomness ciphertext");
8262 Env env{*
this, features};
8263 Account const alice(
"alice"), bob(
"bob"), carol(
"carol");
8267 {{.account = bob}, {.account = carol, .payAmount = 1000, .convertAmount = 50}},
8268 tfMPTCanTransfer | tfMPTCanHoldConfidentialBalance};
8269 auto& mptAlice = confEnv.
mpt;
8271 uint64_t
const sendAmount = 10;
8282 auto const seq = env.seq(bob);
8283 auto jv = mptAlice.sendJV({.account = bob, .dest = carol, .amt = sendAmount}, seq);
8284 auto jtx = env.jt(jv);
8285 BEAST_EXPECT(jtx.stx);
8304 obj.
setFieldVL(sfSenderEncryptedAmount, zeroCiphertext);
8312 auto const jr = env.rpc(
"submit",
strHex(tampered.
slice()));
8313 BEAST_EXPECT(jr[jss::result][jss::error] ==
"invalidTransaction");
8339 .senderEncryptedAmt = zeroCiphertext,
8355 auto const proof1 = setup1.
generateProof(mptAlice, env, bob, carol);
8356 if (!BEAST_EXPECT(proof1.has_value()))
8362 mptAlice.mergeInbox({.account = carol});
8377 testcase(
"Send: recipient inbox rerandomization prevents merge cancellation");
8383 auto getCanonicalZeroBlindingFactor = [](
AccountID const& account,
MPTID const& mptID) {
8404 if (secp256k1_ec_seckey_verify(mpt_secp256k1_context(), scalar.data()))
8413 auto negateScalarSum = [](
Buffer const& lhs,
Buffer const& rhs) {
8416 secp256k1_mpt_scalar_add(
sum.data(), lhs.
data(), rhs.data());
8417 secp256k1_mpt_scalar_negate(negated.
data(),
sum.data());
8425 Env env{*
this, features};
8426 Account const alice(
"alice"), bob(
"bob"), carol(
"carol");
8427 MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
8431 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
8434 mptAlice.authorize({.account = bob});
8435 mptAlice.authorize({.account = carol});
8436 mptAlice.pay(alice, bob, 100);
8437 mptAlice.pay(alice, carol, 100);
8439 mptAlice.generateKeyPair(alice);
8440 mptAlice.generateKeyPair(bob);
8441 mptAlice.generateKeyPair(carol);
8442 mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
8447 .holderPubKey = mptAlice.getPubKey(carol),
8449 mptAlice.mergeInbox({.account = carol});
8455 .holderPubKey = mptAlice.getPubKey(bob),
8456 .blindingFactor = convertBlindingFactor,
8459 Buffer const canonicalZeroBlindingFactor =
8460 getCanonicalZeroBlindingFactor(bob.id(), mptAlice.issuanceID());
8464 Buffer const maliciousSendBlindingFactor =
8465 negateScalarSum(canonicalZeroBlindingFactor, convertBlindingFactor);
8471 .blindingFactor = maliciousSendBlindingFactor,
8474 mptAlice.mergeInbox({.account = bob});
8476 auto const bobSpending =
8477 mptAlice.getDecryptedBalance(bob, MPTTester::holderEncryptedSpending);
8478 BEAST_EXPECT(bobSpending && *bobSpending == 25);
8486 Env env{*
this, features};
8487 Account const alice(
"alice"), bob(
"bob"), carol(
"carol"), auditor(
"auditor");
8488 MPTTester mptAlice(env, alice, {.holders = {bob, carol}, .auditor = auditor});
8492 .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance,
8495 mptAlice.authorize({.account = bob});
8496 mptAlice.authorize({.account = carol});
8497 mptAlice.pay(alice, bob, 100);
8498 mptAlice.pay(alice, carol, 100);
8500 mptAlice.generateKeyPair(alice);
8501 mptAlice.generateKeyPair(bob);
8502 mptAlice.generateKeyPair(carol);
8503 mptAlice.generateKeyPair(auditor);
8506 .issuerPubKey = mptAlice.getPubKey(alice),
8507 .auditorPubKey = mptAlice.getPubKey(auditor),
8513 .holderPubKey = mptAlice.getPubKey(carol),
8515 mptAlice.mergeInbox({.account = carol});
8521 .holderPubKey = mptAlice.getPubKey(bob),
8522 .blindingFactor = convertBlindingFactor,
8526 Buffer const maliciousSendBlindingFactor =
8533 .blindingFactor = maliciousSendBlindingFactor,
8536 auto const bobAuditor =
8537 mptAlice.getDecryptedBalance(bob, MPTTester::auditorEncryptedBalance);
8538 BEAST_EXPECT(bobAuditor && *bobAuditor == 25);
A generic endpoint for log messages.
TestcaseT testcase
Memberspace for declaring test cases.
Like std::vector<char> but better.
std::size_t size() const noexcept
Returns the number of bytes in the buffer.
std::uint8_t const * data() const noexcept
Return a pointer to beginning of the storage.
static Buffer getForgedSingleBulletproof(uint64_t value, Buffer const &blindingFactor, UInt256 const &contextHash)
static constexpr size_t kBulletproofOffset
static Buffer getForgedSendProof(test::jtx::MPTTester &mpt, test::jtx::Env &env, test::jtx::Account const &sender, test::jtx::Account const &dest, ConfidentialSendSetup const &setup)
static T requireOptional(std::optional< T > value, char const *message)
static Buffer const & getBadCiphertext()
static T const & requireOptionalRef(std::optional< T > const &value, char const *message)
static Buffer getForgedBulletproof(std::array< uint64_t, 2 > const &values, std::array< Buffer, 2 > const &blindingFactors, UInt256 const &contextHash)
static std::string getTrivialSendProofHex()
static Buffer getForgedConvertBackProof(test::jtx::MPTTester &mpt, test::jtx::Account const &holder, uint64_t claimedBalance, uint64_t realBalance, uint64_t amt, Buffer const &pedersenCommitment, Buffer const &encryptedSpendingBalance, Buffer const &pcBlindingFactor, UInt256 const &contextHash)
static Buffer const & getTrivialCommitment()
static Buffer const & getTrivialCiphertext()
void testConvertBackPreflight(FeatureBitset features)
void testConvertBackBulletproof(FeatureBitset features)
void testClawbackInvalidProofContextBinding(FeatureBitset features)
void testConfidentialMPTBaseFee(FeatureBitset features)
void testSendRangeProof(FeatureBitset features)
void testConvertBack(FeatureBitset features)
void testPublicTransfersAfterClearingConfidentialFlag(FeatureBitset features)
void testTransferFee(FeatureBitset features)
void testConvert(FeatureBitset features)
void testConvertBackPedersenProof(FeatureBitset features)
void testConvertBackHomomorphicCiphertextModification(FeatureBitset features)
void testSendRerandomizesRecipientInboxAgainstMergeCancellation(FeatureBitset features)
void testConvertIdentityElementRejection(FeatureBitset features)
void testSetPreclaim(FeatureBitset features)
void testSetPreflight(FeatureBitset features)
void testSend(FeatureBitset features)
void testMutatePrivacy(FeatureBitset features)
void testSet(FeatureBitset features)
void testSendCiphertextCombination(FeatureBitset features)
void testSendFiatShamirBinding(FeatureBitset features)
void testSendCiphertextRerandomization(FeatureBitset features)
void testSendPreclaim(FeatureBitset features)
void testConvertBackPreclaim(FeatureBitset features)
void testSendInvalidCurvePoints(FeatureBitset features)
void testDelete(FeatureBitset features)
void testClawbackPreclaim(FeatureBitset features)
void testSendWrongIssuerPublicKey(FeatureBitset features)
void testConvertBackWithAuditor(FeatureBitset features)
void testSendOverdraftBulletproof(FeatureBitset features)
void testSendZeroRandomnessCiphertext(FeatureBitset features)
void testSendCiphertextMalleability(FeatureBitset features)
void testSendPreflight(FeatureBitset features)
void testClawbackPreflight(FeatureBitset features)
void testMergeInboxPreclaim(FeatureBitset features)
void testClawback(FeatureBitset features)
void testSendNegativeValueMalleability(FeatureBitset features)
void testMergeInbox(FeatureBitset features)
void testClawbackProof(FeatureBitset features)
void testConvertBackOverdraftBulletproof(FeatureBitset features)
void testSendCiphertextNegation(FeatureBitset features)
void testSendForgedRangeProof(FeatureBitset features)
void testConvertBackOverdraftBulletproofImpl(FeatureBitset features, uint64_t balance, uint64_t amt)
void testSendInvalidProofContextBinding(FeatureBitset features)
void testSendForgedEqualityProof(FeatureBitset features)
void testClawbackWithAuditor(FeatureBitset features)
void testSendCrossStatementProofSubstitution(FeatureBitset features)
void testSendProofComponentReuse(FeatureBitset features)
void testWithFeats(FeatureBitset features)
void testSendWithAuditor(FeatureBitset features)
void testConvertWithAuditor(FeatureBitset features)
void run() override
Runs the suite.
void testSendHomomorphicOverflow(FeatureBitset features)
void testConvertBackProofCiphertextBinding(FeatureBitset features)
void testSendSharedRandomnessViolation(FeatureBitset features)
void testConvertPreclaim(FeatureBitset features)
void testConvertBackHomomorphicUnderflow(FeatureBitset features)
void testConvertBackInvalidProofContextBinding(FeatureBitset features)
void testConvertInvalidProofContextBinding(FeatureBitset features)
void testSendOverdraftBulletproofImpl(FeatureBitset features, unsigned balance, unsigned amt)
void testMergeInboxPreflight(FeatureBitset features)
void testConvertBackProofVersionMismatch(FeatureBitset features)
void testSendWrongGroupPointInjection(FeatureBitset features)
void testSendZeroAmount(FeatureBitset features)
void testConvertPreflight(FeatureBitset features)
void testSendSpecialWitnessValues(FeatureBitset features)
Writable ledger view that accumulates state and tx changes.
void rawReplace(SLE::Ref sle) override
Unconditionally replace a state item.
SLE::const_pointer read(Keylet const &k) const override
Return the state item associated with a key.
Blob getFieldVL(SField const &field) const
void setFieldVL(SField const &field, Blob const &)
void add(Serializer &s) const override
Slice slice() const noexcept
An immutable linear range of bytes.
void convertBack(MPTConvertBack const &arg=MPTConvertBack{}, std::source_location const &loc=std::source_location::current())
std::optional< uint64_t > getDecryptedBalance(Account const &account, EncryptedBalanceType balanceType) const
std::uint32_t getMPTokenVersion(Account const account) const
void send(MPTConfidentialSend const &arg=MPTConfidentialSend{}, std::source_location const &loc=std::source_location::current())
void pay(Account const &src, Account const &dest, std::int64_t amount, std::optional< TER > err=std::nullopt, std::optional< std::vector< std::string > > credentials=std::nullopt, std::source_location const &loc=std::source_location::current())
void confidentialClaw(MPTConfidentialClawback const &arg=MPTConfidentialClawback{}, std::source_location const &loc=std::source_location::current())
void set(MPTSet const &set={}, std::source_location const &loc=std::source_location::current())
void authorize(MPTAuthorize const &arg=MPTAuthorize{}, std::source_location const &loc=std::source_location::current())
std::optional< Buffer > getPrivKey(Account const &account, std::optional< std::uint32_t > epoch=std::nullopt) const
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Keylet mptokenIssuance(MPTID const &issuanceID) noexcept
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
constexpr std::size_t kEcPubKeyLength
Length of EC public key (compressed).
constexpr std::uint8_t kEcCompressedPrefixEvenY
Compressed EC point prefix for even y-coordinate.
static auto sum(TCollection const &col)
STAmount convertAmount(STAmount const &amt, bool all)
constexpr std::size_t kEcBlindingFactorLength
Length of the EC blinding factor in bytes.
UInt256 getConvertContextHash(AccountID const &account, UInt192 const &issuanceID, std::uint32_t sequence)
Generates the context hash for ConfidentialMPTConvert transactions.
constexpr std::size_t kCompressedEcPointLength
Length of EC point (compressed).
std::string strHex(FwdIt begin, FwdIt end)
UInt256 getSendContextHash(AccountID const &account, UInt192 const &issuanceID, std::uint32_t sequence, AccountID const &destination, std::uint32_t version)
Generates the context hash for ConfidentialMPTSend transactions.
constexpr std::uint32_t kConfidentialFeeMultiplier
Extra base fee multiplier charged to confidential MPT transactions.
UInt256 getConvertBackContextHash(AccountID const &account, UInt192 const &issuanceID, std::uint32_t sequence, std::uint32_t version)
Generates the context hash for ConfidentialMPTConvertBack transactions.
constexpr std::size_t kEcClawbackProofLength
Length of the ZKProof for ConfidentialMPTClawback.
constexpr std::size_t kEcSchnorrProofLength
Length of Schnorr ZKProof for public key registration (compact form) in bytes.
constexpr std::size_t kEcGamalEncryptedTotalLength
EC ElGamal ciphertext length: two compressed EC points concatenated.
std::string to_string(BaseUInt< Bits, Tag > const &a)
constexpr std::size_t kEcConvertBackProofLength
128 bytes compact sigma proof + 688 bytes single bulletproof.
constexpr std::size_t kEcSingleBulletproofLength
Length of single bulletproof (range proof for 1 commitment) in bytes.
constexpr std::size_t kEcPedersenCommitmentLength
Length of Pedersen Commitment (compressed).
constexpr std::size_t kEcCiphertextComponentLength
Length of one compressed EC point component in an EC ElGamal ciphertext.
std::optional< Buffer > homomorphicSubtract(Slice const &a, Slice const &b)
Homomorphically subtracts two ElGamal ciphertexts.
constexpr std::size_t kEcDoubleBulletproofLength
Length of double bulletproof (range proof for 2 commitments) in bytes.
BaseUInt< 192 > MPTID
MPTID is a 192-bit value representing MPT Issuance ID, which is a concatenation of a 32-bit sequence ...
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
UInt256 getClawbackContextHash(AccountID const &account, UInt192 const &issuanceID, std::uint32_t sequence, AccountID const &holder)
Generates the context hash for ConfidentialMPTClawback transactions.
Buffer generateBlindingFactor()
Generates a cryptographically secure blinding factor (size=xrpl::kEcBlindingFactorLength).
MPTID makeMptID(std::uint32_t const sequence, AccountID const &account)
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
constexpr FlagValue tifMPTCanHoldConfidentialBalance
constexpr std::size_t kEcSendProofLength
192 bytes compact sigma proof + 754 bytes double bulletproof.
TERSubset< CanCvtToTER > TER
constexpr FlagValue tifMPTCanLock
constexpr std::uint64_t kMaxMpTokenAmount
The maximum amount of MPTokenIssuance.
BEAST_DEFINE_TESTSUITE(AccountTxPaging, app, xrpl)
std::optional< Buffer > homomorphicAdd(Slice const &a, Slice const &b)
Homomorphically adds two ElGamal ciphertexts.
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
T const_pointer_cast(T... args)
Buffer amountBlindingFactor
std::optional< Buffer > generateProof(test::jtx::MPTTester &mpt, test::jtx::Env &env, test::jtx::Account const &sender, test::jtx::Account const &dest) const
std::vector< ConfidentialRecipient > recipients
test::jtx::MPTConfidentialSend sendArgs(test::jtx::Account const &sender, test::jtx::Account const &dest, Buffer const &proof, std::optional< TER > err=std::nullopt) const
std::optional< Buffer > auditorAmt
Buffer balanceBlindingFactor
std::optional< Buffer > amountCommitment
std::optional< Buffer > senderEncryptedAmt
std::optional< Buffer > destEncryptedAmt
std::optional< Buffer > issuerEncryptedAmt