This commit is contained in:
yinyiqian1
2026-09-01 18:16:32 -04:00
parent fee81fde05
commit 7f169ef864
10 changed files with 11 additions and 138 deletions

View File

@@ -540,6 +540,11 @@ constexpr std::size_t kEcConvertBackProofLength =
*/
constexpr std::size_t kEcClawbackProofLength = SECP256K1_COMPACT_CLAWBACK_PROOF_SIZE;
/**
* Length of compact equality proof.
*/
constexpr std::size_t kEcEqualityProofLength = 128;
/**
* Extra base fee multiplier charged to confidential MPT transactions.
*/

View File

@@ -410,6 +410,7 @@ LEDGER_ENTRY(ltMPTOKEN_ISSUANCE, 0x007e, MPTokenIssuance, mpt_issuance, ({
{sfAuditorEncryptionKey, SoeOptional},
{sfIssuerKeyEpoch, SoeOptional},
{sfAuditorKeyEpoch, SoeOptional},
{sfInitialIssuerEncryptionKey, SoeOptional},
{sfConfidentialOutstandingAmount, SoeDefault},
}))
@@ -431,6 +432,7 @@ LEDGER_ENTRY(ltMPTOKEN, 0x007f, MPToken, mptoken, ({
{sfAuditorEncryptedBalance, SoeOptional},
{sfIssuerKeyMirrorEpoch, SoeOptional},
{sfAuditorKeyMirrorEpoch, SoeOptional},
{sfIssuerMirrorEncryptionKey, SoeOptional},
{sfHolderEncryptionKey, SoeOptional},
}))

View File

@@ -322,7 +322,8 @@ TYPED_SFIELD(sfAuditorEncryptedAmount, VL, 43)
TYPED_SFIELD(sfAuditorEncryptionKey, VL, 44)
TYPED_SFIELD(sfAmountCommitment, VL, 45)
TYPED_SFIELD(sfBalanceCommitment, VL, 46)
TYPED_SFIELD(sfPreviousIssuerEncryptionKey, VL, 47)
TYPED_SFIELD(sfInitialIssuerEncryptionKey, VL, 47)
TYPED_SFIELD(sfIssuerMirrorEncryptionKey, VL, 48)
// account (common)
TYPED_SFIELD(sfAccount, ACCOUNT, 1)

View File

@@ -1143,7 +1143,6 @@ TRANSACTION(ttCONFIDENTIAL_MPT_MIRROR_UPDATE, 92, ConfidentialMPTMirrorUpdate,
{sfHolder, SoeOptional},
{sfIssuerEncryptedAmount, SoeOptional},
{sfAuditorEncryptedAmount, SoeOptional},
{sfPreviousIssuerEncryptionKey, SoeOptional},
{sfZKProof, SoeRequired},
}))

View File

@@ -136,32 +136,6 @@ public:
return this->tx_->isFieldPresent(sfAuditorEncryptedAmount);
}
/**
* @brief Get sfPreviousIssuerEncryptionKey (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getPreviousIssuerEncryptionKey() const
{
if (hasPreviousIssuerEncryptionKey())
{
return this->tx_->at(sfPreviousIssuerEncryptionKey);
}
return std::nullopt;
}
/**
* @brief Check if sfPreviousIssuerEncryptionKey is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasPreviousIssuerEncryptionKey() const
{
return this->tx_->isFieldPresent(sfPreviousIssuerEncryptionKey);
}
/**
* @brief Get sfZKProof (SoeRequired)
* @return The field value.
@@ -264,17 +238,6 @@ public:
return *this;
}
/**
* @brief Set sfPreviousIssuerEncryptionKey (SoeOptional)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTMirrorUpdateBuilder&
setPreviousIssuerEncryptionKey(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfPreviousIssuerEncryptionKey] = value;
return *this;
}
/**
* @brief Set sfZKProof (SoeRequired)
* @return Reference to this builder for method chaining.

View File

@@ -62,14 +62,6 @@ ConfidentialMPTMirrorUpdate::preflight(PreflightContext const& ctx)
if (!hasIssuerAmount && !hasAuditorAmount)
return temMALFORMED;
// The previous issuer key is required exactly for an issuer-mode
// re-encryption of the issuer mirror,either issuer key rotation or
// issuer/auditor key simultaneous rotation.
bool const hasPreviousIssuerKey = ctx.tx.isFieldPresent(sfPreviousIssuerEncryptionKey);
bool const needsPreviousIssuerKey = hasHolder && hasIssuerAmount;
if (hasPreviousIssuerKey != needsPreviousIssuerKey)
return temMALFORMED;
// Check the length of the encrypted amounts. Length check is cheaper than format check so put
// it before the format check.
if (hasIssuerAmount && ctx.tx[sfIssuerEncryptedAmount].length() != kEcGamalEncryptedTotalLength)
@@ -86,10 +78,6 @@ ConfidentialMPTMirrorUpdate::preflight(PreflightContext const& ctx)
if (hasAuditorAmount && !isValidCiphertext(ctx.tx[sfAuditorEncryptedAmount]))
return temBAD_CIPHERTEXT;
// If previous issuer key is present, it must be a valid EC point.
if (hasPreviousIssuerKey && !isValidCompressedECPoint(ctx.tx[sfPreviousIssuerEncryptionKey]))
return temMALFORMED;
// todo: check zkproof
return tesSUCCESS;

View File

@@ -10,7 +10,6 @@
#include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/TER.h>
@@ -775,17 +774,11 @@ class ConfidentialMPTKeyRotation_test : public ConfidentialTransferTestBase
.ownerCount = 1,
.flags = tfMPTCanTransfer | tfMPTCanHoldConfidentialBalance,
});
mptAlice.generateKeyPair(alice);
// A valid EC point
auto const validKey = mptAlice.getPubKey(alice);
// Issuer mode but account is not the issuer.
mptAlice.mirrorUpdate({
.account = bob,
.holder = carol,
.issuerEncryptedAmount = validCipher,
.previousIssuerKey = validKey,
.err = temMALFORMED,
});
@@ -794,7 +787,6 @@ class ConfidentialMPTKeyRotation_test : public ConfidentialTransferTestBase
.account = alice,
.holder = alice,
.issuerEncryptedAmount = validCipher,
.previousIssuerKey = validKey,
.err = temMALFORMED,
});
@@ -812,37 +804,11 @@ class ConfidentialMPTKeyRotation_test : public ConfidentialTransferTestBase
.err = temMALFORMED,
});
// Holder mode, previousIssuerKey should not be present.
mptAlice.mirrorUpdate({
.account = bob,
.issuerEncryptedAmount = validCipher,
.previousIssuerKey = validKey,
.err = temMALFORMED,
});
// Issuer mode, previousIssuerKey is provided but issuerEncryptedAmount is missing.
mptAlice.mirrorUpdate({
.account = alice,
.holder = bob,
.auditorEncryptedAmount = validCipher,
.previousIssuerKey = validKey,
.err = temMALFORMED,
});
// Issuer mode, issuerEncryptedAmount is present but previousIssuerKey is missing.
mptAlice.mirrorUpdate({
.account = alice,
.holder = bob,
.issuerEncryptedAmount = validCipher,
.err = temMALFORMED,
});
// Issuer amount has the wrong length.
mptAlice.mirrorUpdate({
.account = alice,
.holder = bob,
.issuerEncryptedAmount = gMakeZeroBuffer(10),
.previousIssuerKey = validKey,
.err = temBAD_CIPHERTEXT,
});
@@ -859,7 +825,6 @@ class ConfidentialMPTKeyRotation_test : public ConfidentialTransferTestBase
.account = alice,
.holder = bob,
.issuerEncryptedAmount = getBadCiphertext(),
.previousIssuerKey = validKey,
.err = temBAD_CIPHERTEXT,
});
@@ -869,18 +834,8 @@ class ConfidentialMPTKeyRotation_test : public ConfidentialTransferTestBase
.holder = bob,
.issuerEncryptedAmount = validCipher,
.auditorEncryptedAmount = getBadCiphertext(),
.previousIssuerKey = validKey,
.err = temBAD_CIPHERTEXT,
});
// previousIssuerKey is present but not a valid EC point.
mptAlice.mirrorUpdate({
.account = alice,
.holder = bob,
.issuerEncryptedAmount = validCipher,
.previousIssuerKey = gMakeZeroBuffer(kEcPubKeyLength),
.err = temMALFORMED,
});
}
void
@@ -890,7 +845,6 @@ class ConfidentialMPTKeyRotation_test : public ConfidentialTransferTestBase
using namespace test::jtx;
Buffer const& validCipher = getTrivialCiphertext();
Buffer const& validKey = getTrivialCommitment();
// The issuance does not exist.
{
@@ -924,7 +878,6 @@ class ConfidentialMPTKeyRotation_test : public ConfidentialTransferTestBase
.account = alice,
.holder = bob,
.issuerEncryptedAmount = validCipher,
.previousIssuerKey = validKey,
.err = tecNO_PERMISSION,
});
}
@@ -943,7 +896,6 @@ class ConfidentialMPTKeyRotation_test : public ConfidentialTransferTestBase
.account = alice,
.holder = bob,
.issuerEncryptedAmount = validCipher,
.previousIssuerKey = validKey,
.err = tecNO_PERMISSION,
});
}
@@ -964,7 +916,6 @@ class ConfidentialMPTKeyRotation_test : public ConfidentialTransferTestBase
.account = alice,
.holder = carol,
.issuerEncryptedAmount = validCipher,
.previousIssuerKey = validKey,
.err = tecNO_TARGET,
});
}
@@ -984,7 +935,6 @@ class ConfidentialMPTKeyRotation_test : public ConfidentialTransferTestBase
.account = alice,
.holder = bob,
.issuerEncryptedAmount = validCipher,
.previousIssuerKey = validKey,
.err = tecOBJECT_NOT_FOUND,
});
}
@@ -1005,7 +955,6 @@ class ConfidentialMPTKeyRotation_test : public ConfidentialTransferTestBase
.account = alice,
.holder = bob,
.issuerEncryptedAmount = validCipher,
.previousIssuerKey = validKey,
.err = tecNO_PERMISSION,
});
}
@@ -1039,7 +988,6 @@ class ConfidentialMPTKeyRotation_test : public ConfidentialTransferTestBase
.account = alice,
.holder = bob,
.issuerEncryptedAmount = validCipher,
.previousIssuerKey = validKey,
.err = tecNO_PERMISSION,
});
}
@@ -1129,7 +1077,6 @@ class ConfidentialMPTKeyRotation_test : public ConfidentialTransferTestBase
.holder = bob,
.issuerEncryptedAmount = validCipher,
.auditorEncryptedAmount = validCipher,
.previousIssuerKey = validKey,
.err = tecNO_PERMISSION,
});
}
@@ -1162,7 +1109,6 @@ class ConfidentialMPTKeyRotation_test : public ConfidentialTransferTestBase
.holder = bob,
.issuerEncryptedAmount = validCipher,
.auditorEncryptedAmount = validCipher,
.previousIssuerKey = validKey,
.err = tecNO_PERMISSION,
});
}
@@ -1294,7 +1240,7 @@ class ConfidentialMPTKeyRotation_test : public ConfidentialTransferTestBase
std::uint64_t const amount = 100;
// Issuer mode issuer-mirror migration. The new issuer mirror is written
// and the auissuerditor mirror epoch advances to the issuer key epoch.
// and the auditor mirror epoch advances to the issuer key epoch.
{
Env env{*this, features};
Account const alice("alice");
@@ -1316,7 +1262,6 @@ class ConfidentialMPTKeyRotation_test : public ConfidentialTransferTestBase
.account = alice,
.holder = bob,
.issuerEncryptedAmount = newIssuerCipher,
.previousIssuerKey = ct.mpt.getPubKey(alice),
});
auto const sle = env.le(keylet::mptoken(ct.mpt.issuanceID(), bob.id()));
@@ -1331,7 +1276,6 @@ class ConfidentialMPTKeyRotation_test : public ConfidentialTransferTestBase
.account = alice,
.holder = bob,
.issuerEncryptedAmount = newIssuerCipher,
.previousIssuerKey = ct.mpt.getPubKey(alice),
.err = tecNO_PERMISSION,
});
}
@@ -1408,7 +1352,6 @@ class ConfidentialMPTKeyRotation_test : public ConfidentialTransferTestBase
.holder = bob,
.issuerEncryptedAmount = newIssuerCipher,
.auditorEncryptedAmount = newAuditorCipher,
.previousIssuerKey = ct.mpt.getPubKey(alice),
});
auto const sle = env.le(keylet::mptoken(ct.mpt.issuanceID(), bob.id()));
@@ -1619,7 +1562,6 @@ class ConfidentialMPTKeyRotation_test : public ConfidentialTransferTestBase
.account = alice,
.holder = bob,
.issuerEncryptedAmount = newIssuerCipher,
.previousIssuerKey = ct.mpt.getPubKey(alice),
});
{
@@ -1636,7 +1578,6 @@ class ConfidentialMPTKeyRotation_test : public ConfidentialTransferTestBase
.account = alice,
.holder = bob,
.issuerEncryptedAmount = newIssuerCipher,
.previousIssuerKey = ct.mpt.getPubKey(alice),
.err = tecNO_PERMISSION,
});
@@ -1727,7 +1668,6 @@ class ConfidentialMPTKeyRotation_test : public ConfidentialTransferTestBase
.holder = bob,
.issuerEncryptedAmount = bothIssuerCipher,
.auditorEncryptedAmount = bothAuditorCipher,
.previousIssuerKey = ct.mpt.getPubKey(issuerKey3),
});
{
@@ -1747,7 +1687,6 @@ class ConfidentialMPTKeyRotation_test : public ConfidentialTransferTestBase
.holder = bob,
.issuerEncryptedAmount = bothIssuerCipher,
.auditorEncryptedAmount = bothAuditorCipher,
.previousIssuerKey = ct.mpt.getPubKey(issuerKey5),
.err = tecNO_PERMISSION,
});
}

View File

@@ -2588,8 +2588,6 @@ MPTTester::mirrorUpdate(MPTMirrorUpdate const& arg)
jv[sfIssuerEncryptedAmount] = strHex(*arg.issuerEncryptedAmount);
if (arg.auditorEncryptedAmount)
jv[sfAuditorEncryptedAmount] = strHex(*arg.auditorEncryptedAmount);
if (arg.previousIssuerKey)
jv[sfPreviousIssuerEncryptionKey] = strHex(*arg.previousIssuerKey);
// Placeholder for proof, the logic will be added in the future
if (arg.zkProof)
@@ -2598,7 +2596,7 @@ MPTTester::mirrorUpdate(MPTMirrorUpdate const& arg)
}
else
{
jv[sfZKProof] = strHex(gMakeZeroBuffer(kEcGamalEncryptedTotalLength));
jv[sfZKProof] = strHex(gMakeZeroBuffer(kEcEqualityProofLength));
}
submit(arg, jv);

View File

@@ -371,7 +371,6 @@ struct MPTMirrorUpdate
std::optional<MPTID> id = std::nullopt;
std::optional<Buffer> issuerEncryptedAmount = std::nullopt;
std::optional<Buffer> auditorEncryptedAmount = std::nullopt;
std::optional<Buffer> previousIssuerKey = std::nullopt;
std::optional<Buffer> zkProof = std::nullopt;
std::optional<XRPAmount> fee = std::nullopt;
std::optional<std::uint32_t> flags = std::nullopt;

View File

@@ -33,7 +33,6 @@ TEST(TransactionsConfidentialMPTMirrorUpdateTests, BuilderSettersRoundTrip)
auto const holderValue = canonical_ACCOUNT();
auto const issuerEncryptedAmountValue = canonical_VL();
auto const auditorEncryptedAmountValue = canonical_VL();
auto const previousIssuerEncryptionKeyValue = canonical_VL();
auto const zKProofValue = canonical_VL();
ConfidentialMPTMirrorUpdateBuilder builder{
@@ -48,7 +47,6 @@ TEST(TransactionsConfidentialMPTMirrorUpdateTests, BuilderSettersRoundTrip)
builder.setHolder(holderValue);
builder.setIssuerEncryptedAmount(issuerEncryptedAmountValue);
builder.setAuditorEncryptedAmount(auditorEncryptedAmountValue);
builder.setPreviousIssuerEncryptionKey(previousIssuerEncryptionKeyValue);
auto tx = builder.build(publicKey, secretKey);
@@ -102,14 +100,6 @@ TEST(TransactionsConfidentialMPTMirrorUpdateTests, BuilderSettersRoundTrip)
EXPECT_TRUE(tx.hasAuditorEncryptedAmount());
}
{
auto const& expected = previousIssuerEncryptionKeyValue;
auto const actualOpt = tx.getPreviousIssuerEncryptionKey();
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfPreviousIssuerEncryptionKey should be present";
expectEqualField(expected, *actualOpt, "sfPreviousIssuerEncryptionKey");
EXPECT_TRUE(tx.hasPreviousIssuerEncryptionKey());
}
}
// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper,
@@ -130,7 +120,6 @@ TEST(TransactionsConfidentialMPTMirrorUpdateTests, BuilderFromStTxRoundTrip)
auto const holderValue = canonical_ACCOUNT();
auto const issuerEncryptedAmountValue = canonical_VL();
auto const auditorEncryptedAmountValue = canonical_VL();
auto const previousIssuerEncryptionKeyValue = canonical_VL();
auto const zKProofValue = canonical_VL();
// Build an initial transaction
@@ -145,7 +134,6 @@ TEST(TransactionsConfidentialMPTMirrorUpdateTests, BuilderFromStTxRoundTrip)
initialBuilder.setHolder(holderValue);
initialBuilder.setIssuerEncryptedAmount(issuerEncryptedAmountValue);
initialBuilder.setAuditorEncryptedAmount(auditorEncryptedAmountValue);
initialBuilder.setPreviousIssuerEncryptionKey(previousIssuerEncryptionKeyValue);
auto initialTx = initialBuilder.build(publicKey, secretKey);
@@ -197,13 +185,6 @@ TEST(TransactionsConfidentialMPTMirrorUpdateTests, BuilderFromStTxRoundTrip)
expectEqualField(expected, *actualOpt, "sfAuditorEncryptedAmount");
}
{
auto const& expected = previousIssuerEncryptionKeyValue;
auto const actualOpt = rebuiltTx.getPreviousIssuerEncryptionKey();
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfPreviousIssuerEncryptionKey should be present";
expectEqualField(expected, *actualOpt, "sfPreviousIssuerEncryptionKey");
}
}
// 3) Verify wrapper throws when constructed from wrong transaction type.
@@ -269,8 +250,6 @@ TEST(TransactionsConfidentialMPTMirrorUpdateTests, OptionalFieldsReturnNullopt)
EXPECT_FALSE(tx.getIssuerEncryptedAmount().has_value());
EXPECT_FALSE(tx.hasAuditorEncryptedAmount());
EXPECT_FALSE(tx.getAuditorEncryptedAmount().has_value());
EXPECT_FALSE(tx.hasPreviousIssuerEncryptionKey());
EXPECT_FALSE(tx.getPreviousIssuerEncryptionKey().has_value());
}
}