Increase confidential transactions fee (#7063)

This commit is contained in:
yinyiqian1
2026-05-06 17:15:39 -04:00
committed by GitHub
parent b831b661ed
commit ed6de36cb4
16 changed files with 275 additions and 30 deletions

View File

@@ -348,6 +348,9 @@ std::size_t constexpr kEC_CONVERT_BACK_PROOF_LENGTH = 816;
/** Length of the ZKProof for ConfidentialMPTClawback. */
std::size_t constexpr kEC_CLAWBACK_PROOF_LENGTH = 64;
/** Extra base fee multiplier charged to confidential MPT transactions. */
std::uint32_t constexpr kCONFIDENTIAL_FEE_MULTIPLIER = 9;
/** Compressed EC point prefix for even y-coordinate */
std::uint8_t constexpr kEC_COMPRESSED_PREFIX_EVEN_Y = 0x02;

View File

@@ -32,6 +32,9 @@ public:
static NotTEC
preflight(PreflightContext const& ctx);
static XRPAmount
calculateBaseFee(ReadView const& view, STTx const& tx);
static TER
preclaim(PreclaimContext const& ctx);

View File

@@ -34,6 +34,9 @@ public:
static NotTEC
preflight(PreflightContext const& ctx);
static XRPAmount
calculateBaseFee(ReadView const& view, STTx const& tx);
static TER
preclaim(PreclaimContext const& ctx);

View File

@@ -35,6 +35,9 @@ public:
static NotTEC
preflight(PreflightContext const& ctx);
static XRPAmount
calculateBaseFee(ReadView const& view, STTx const& tx);
static TER
preclaim(PreclaimContext const& ctx);

View File

@@ -36,6 +36,9 @@ public:
static NotTEC
preflight(PreflightContext const& ctx);
static XRPAmount
calculateBaseFee(ReadView const& view, STTx const& tx);
static TER
preclaim(PreclaimContext const& ctx);

View File

@@ -45,6 +45,9 @@ public:
static NotTEC
preflight(PreflightContext const& ctx);
static XRPAmount
calculateBaseFee(ReadView const& view, STTx const& tx);
static TER
preclaim(PreclaimContext const& ctx);

View File

@@ -2,12 +2,14 @@
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
@@ -45,6 +47,15 @@ ConfidentialMPTClawback::preflight(PreflightContext const& ctx)
return tesSUCCESS;
}
XRPAmount
ConfidentialMPTClawback::calculateBaseFee(ReadView const& view, STTx const& tx)
{
// Transactor::calculateBaseFee = baseFee + (signerCount * baseFee).
// We charge kCONFIDENTIAL_FEE_MULTIPLIER extra base fees so the total is
// 10 * baseFee + (signerCount * baseFee).
return Transactor::calculateBaseFee(view, tx) + view.fees().base * kCONFIDENTIAL_FEE_MULTIPLIER;
}
TER
ConfidentialMPTClawback::preclaim(PreclaimContext const& ctx)
{

View File

@@ -3,11 +3,13 @@
#include <xrpl/basics/Slice.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/MPTIssue.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/TER.h>
@@ -63,6 +65,15 @@ ConfidentialMPTConvert::preflight(PreflightContext const& ctx)
return tesSUCCESS;
}
XRPAmount
ConfidentialMPTConvert::calculateBaseFee(ReadView const& view, STTx const& tx)
{
// Transactor::calculateBaseFee = baseFee + (signerCount * baseFee).
// We charge kCONFIDENTIAL_FEE_MULTIPLIER extra base fees so the total is
// 10 * baseFee + (signerCount * baseFee).
return Transactor::calculateBaseFee(view, tx) + view.fees().base * kCONFIDENTIAL_FEE_MULTIPLIER;
}
TER
ConfidentialMPTConvert::preclaim(PreclaimContext const& ctx)
{

View File

@@ -2,6 +2,7 @@
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/protocol/Feature.h>
@@ -48,6 +49,15 @@ ConfidentialMPTConvertBack::preflight(PreflightContext const& ctx)
return tesSUCCESS;
}
XRPAmount
ConfidentialMPTConvertBack::calculateBaseFee(ReadView const& view, STTx const& tx)
{
// Transactor::calculateBaseFee = baseFee + (signerCount * baseFee).
// We charge kCONFIDENTIAL_FEE_MULTIPLIER extra base fees so the total is
// 10 * baseFee + (signerCount * baseFee).
return Transactor::calculateBaseFee(view, tx) + view.fees().base * kCONFIDENTIAL_FEE_MULTIPLIER;
}
/**
* Verifies the cryptographic proofs for a ConvertBack transaction.
*

View File

@@ -2,11 +2,13 @@
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TER.h>
@@ -31,6 +33,15 @@ ConfidentialMPTMergeInbox::preflight(PreflightContext const& ctx)
return tesSUCCESS;
}
XRPAmount
ConfidentialMPTMergeInbox::calculateBaseFee(ReadView const& view, STTx const& tx)
{
// Transactor::calculateBaseFee = baseFee + (signerCount * baseFee).
// We charge kCONFIDENTIAL_FEE_MULTIPLIER extra base fees so the total is
// 10 * baseFee + (signerCount * baseFee).
return Transactor::calculateBaseFee(view, tx) + view.fees().base * kCONFIDENTIAL_FEE_MULTIPLIER;
}
TER
ConfidentialMPTMergeInbox::preclaim(PreclaimContext const& ctx)
{

View File

@@ -1,6 +1,7 @@
#include <xrpl/tx/transactors/token/ConfidentialMPTSend.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/ConfidentialTransfer.h>
@@ -88,6 +89,15 @@ ConfidentialMPTSend::preflight(PreflightContext const& ctx)
return tesSUCCESS;
}
XRPAmount
ConfidentialMPTSend::calculateBaseFee(ReadView const& view, STTx const& tx)
{
// Transactor::calculateBaseFee = baseFee + (signerCount * baseFee).
// We charge kCONFIDENTIAL_FEE_MULTIPLIER extra base fees so the total is
// 10 * baseFee + (signerCount * baseFee).
return Transactor::calculateBaseFee(view, tx) + view.fees().base * kCONFIDENTIAL_FEE_MULTIPLIER;
}
TER
verifySendProofs(
PreclaimContext const& ctx,

View File

@@ -2900,7 +2900,8 @@ class ConfidentialTransfer_test : public beast::unit_test::Suite
ConfidentialEnv confEnv{
env,
alice,
{{.account = bob, .payAmount = 1000, .convertAmount = 60}, {carol, 1000, 50}}};
{{.account = bob, .payAmount = 1000, .convertAmount = 60},
{.account = carol, .payAmount = 1000, .convertAmount = 50}}};
auto& mptAlice = confEnv.mpt;
{
@@ -2972,7 +2973,8 @@ class ConfidentialTransfer_test : public beast::unit_test::Suite
ConfidentialEnv zeroEnv{
env2,
alice2,
{{.account = bob2, .payAmount = 100, .convertAmount = 0}, {carol2, 50, 0}}};
{{.account = bob2, .payAmount = 100, .convertAmount = 0},
{.account = carol2, .payAmount = 50, .convertAmount = 0}}};
auto& mptAlice2 = zeroEnv.mpt;
// Trying to send any amount with 0 spending balance must fail:
@@ -6057,7 +6059,8 @@ class ConfidentialTransfer_test : public beast::unit_test::Suite
ConfidentialEnv confEnv{
env,
alice,
{{.account = bob, .payAmount = 100, .convertAmount = 100}, {carol, 50, 50}}};
{{.account = bob, .payAmount = 100, .convertAmount = 100},
{.account = carol, .payAmount = 50, .convertAmount = 50}}};
auto& mptAlice = confEnv.mpt;
// Bob sends 10 to carol. The send amount (10) and Bob's remaining balance
@@ -6205,7 +6208,8 @@ class ConfidentialTransfer_test : public beast::unit_test::Suite
ConfidentialEnv confEnv{
env,
alice,
{{.account = bob, .payAmount = 100, .convertAmount = 60}, {carol, 50, 30}}};
{{.account = bob, .payAmount = 100, .convertAmount = 60},
{.account = carol, .payAmount = 50, .convertAmount = 30}}};
auto& mptAlice = confEnv.mpt;
// sender's encrypted amount has an invalid coordinate
@@ -6284,7 +6288,8 @@ class ConfidentialTransfer_test : public beast::unit_test::Suite
ConfidentialEnv confEnv{
env,
alice,
{{.account = bob, .payAmount = 100, .convertAmount = 60}, {carol, 50, 30}}};
{{.account = bob, .payAmount = 100, .convertAmount = 60},
{.account = carol, .payAmount = 50, .convertAmount = 30}}};
auto& mptAlice = confEnv.mpt;
Buffer badProof(kEC_SEND_PROOF_LENGTH);
@@ -6310,7 +6315,8 @@ class ConfidentialTransfer_test : public beast::unit_test::Suite
ConfidentialEnv confEnv{
env,
alice,
{{.account = bob, .payAmount = 100, .convertAmount = 60}, {carol, 50, 30}}};
{{.account = bob, .payAmount = 100, .convertAmount = 60},
{.account = carol, .payAmount = 50, .convertAmount = 30}}};
auto& mptAlice = confEnv.mpt;
// getTrivialCiphertext() has both C1 and C2 as valid (but trivial)
@@ -6402,7 +6408,10 @@ class ConfidentialTransfer_test : public beast::unit_test::Suite
Env env{*this, features};
Account const alice("alice"), bob("bob"), carol("carol");
ConfidentialEnv confEnv{
env, alice, {{.account = bob, .payAmount = 100, .convertAmount = 60}, {carol, 50, 30}}};
env,
alice,
{{.account = bob, .payAmount = 100, .convertAmount = 60},
{.account = carol, .payAmount = 50, .convertAmount = 30}}};
auto& mptAlice = confEnv.mpt;
// The x-coordinate of the NIST P-256 generator point — a real,
@@ -6586,7 +6595,8 @@ class ConfidentialTransfer_test : public beast::unit_test::Suite
ConfidentialEnv confEnv{
env,
alice,
{{.account = bob, .payAmount = 100, .convertAmount = 100}, {carol, 50, 50}}};
{{.account = bob, .payAmount = 100, .convertAmount = 100},
{.account = carol, .payAmount = 50, .convertAmount = 50}}};
auto& mptAlice = confEnv.mpt;
auto const bobSpendingBefore =
@@ -6733,6 +6743,125 @@ class ConfidentialTransfer_test : public beast::unit_test::Suite
submitWithDivergentC1(Participant::Auditor);
}
void
testConfidentialMPTBaseFee(FeatureBitset features)
{
testcase("test confidential transactions fee");
using namespace test::jtx;
auto setup =
[&](MPTTester& mpt, Account const& alice, Account const& bob, Account const& carol) {
mpt.create({
.ownerCount = 1,
.flags = tfMPTCanLock | tfMPTCanConfidentialAmount | tfMPTCanTransfer |
tfMPTCanClawback,
});
mpt.authorize({.account = bob});
mpt.authorize({.account = carol});
mpt.pay(alice, bob, 100);
mpt.pay(alice, carol, 50);
mpt.generateKeyPair(alice);
mpt.generateKeyPair(bob);
mpt.generateKeyPair(carol);
mpt.set({.account = alice, .issuerPubKey = mpt.getPubKey(alice)});
};
// test expected base fee for confidential transactions
{
Env env{*this, features};
Account const alice("alice"), bob("bob"), carol("carol");
MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
setup(mptAlice, alice, bob, carol);
auto const baseFee = env.current()->fees().base;
auto const expectedFee = baseFee * 10;
// lambda function to submit confidential transaction and check fee charged to the
// account
auto checkFee = [&](Account const& acct, auto&& submitFn) {
auto const before = env.balance(acct);
submitFn();
auto const after = env.balance(acct);
BEAST_EXPECT(before - after == expectedFee);
};
checkFee(bob, [&]() {
mptAlice.convert(
{.account = bob,
.amt = 50,
.holderPubKey = mptAlice.getPubKey(bob),
.fee = expectedFee});
});
checkFee(carol, [&]() {
mptAlice.convert(
{.account = carol,
.amt = 10,
.holderPubKey = mptAlice.getPubKey(carol),
.fee = expectedFee});
});
checkFee(bob, [&]() { mptAlice.mergeInbox({.account = bob, .fee = expectedFee}); });
checkFee(carol, [&]() { mptAlice.mergeInbox({.account = carol, .fee = expectedFee}); });
checkFee(bob, [&]() {
mptAlice.send({.account = bob, .dest = carol, .amt = 5, .fee = expectedFee});
});
checkFee(bob, [&]() {
mptAlice.convertBack({.account = bob, .amt = 5, .fee = expectedFee});
});
checkFee(alice, [&]() {
mptAlice.confidentialClaw(
{.account = alice, .holder = carol, .amt = 15, .fee = expectedFee});
});
}
// test insufficient fee for confidential transactions
{
Env env{*this, features};
Account const alice("alice"), bob("bob"), carol("carol");
MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
setup(mptAlice, alice, bob, carol);
auto const baseFee = env.current()->fees().base;
mptAlice.convert(
{.account = bob,
.amt = 1,
.holderPubKey = mptAlice.getPubKey(bob),
.fee = baseFee * 10 - 1,
.err = telINSUF_FEE_P});
mptAlice.mergeInbox({.account = bob, .fee = baseFee, .err = telINSUF_FEE_P});
mptAlice.send(
{.account = bob,
.dest = carol,
.amt = 1,
.fee = baseFee * 9,
.err = telINSUF_FEE_P});
mptAlice.convertBack({.account = bob, .amt = 1, .fee = baseFee, .err = telINSUF_FEE_P});
mptAlice.confidentialClaw(
{.account = alice,
.holder = carol,
.amt = 1,
.fee = baseFee,
.err = telINSUF_FEE_P});
}
// test excessive fee for confidential transactions
{
Env env{*this, features};
Account const alice("alice"), bob("bob"), carol("carol");
MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
setup(mptAlice, alice, bob, carol);
auto const baseFee = env.current()->fees().base;
auto const highFee = baseFee * 20;
auto const bobBefore = env.balance(bob);
mptAlice.convert(
{.account = bob,
.amt = 1,
.holderPubKey = mptAlice.getPubKey(bob),
.fee = highFee});
BEAST_EXPECT(env.balance(bob) == bobBefore - highFee);
}
}
// Exercises every Confidential Transfer transaction type (MPTokenIssuanceSet,
// Convert, MergeInbox, Send, ConvertBack) using tickets instead of regular account
// sequence numbers.
@@ -7082,7 +7211,7 @@ class ConfidentialTransfer_test : public beast::unit_test::Suite
auto const bobSeq = env.seq(bob);
auto const carolSeq = env.seq(carol);
// 3 signers, Bob, Carol, Dave
auto const batchFee = batch::calcBatchFee(env, 1, 3);
auto const batchFee = batch::calcConfidentialBatchFee(env, 1, 3);
auto const jv1 = mpt.sendJV({.account = bob, .dest = carol, .amt = 100}, bobSeq + 1);
auto const jv2 = mpt.mergeInboxJV({.account = carol});
@@ -7119,7 +7248,7 @@ class ConfidentialTransfer_test : public beast::unit_test::Suite
// tfAllOrNothing — rejects the whole batch as 2nd txn proof is incorrect
{
auto const bobSeq = env.seq(bob);
auto const batchFee = batch::calcBatchFee(env, 0, 2);
auto const batchFee = batch::calcConfidentialBatchFee(env, 0, 2);
auto const jv1 = mpt.sendJV({.account = bob, .dest = carol, .amt = 50}, bobSeq + 1);
auto const jv2 = mpt.sendJV({.account = bob, .dest = dave, .amt = 60}, bobSeq + 2);
@@ -7140,7 +7269,7 @@ class ConfidentialTransfer_test : public beast::unit_test::Suite
// If we change batch mode to be tfIndependent — txn 1 applies, inner 2 fails.
{
auto const bobSeq = env.seq(bob);
auto const batchFee = batch::calcBatchFee(env, 0, 2);
auto const batchFee = batch::calcConfidentialBatchFee(env, 0, 2);
auto const jv1 = mpt.sendJV({.account = bob, .dest = carol, .amt = 50}, bobSeq + 1);
auto const jv2 = mpt.sendJV({.account = bob, .dest = dave, .amt = 60}, bobSeq + 2);
@@ -7176,7 +7305,7 @@ class ConfidentialTransfer_test : public beast::unit_test::Suite
{
auto const bobSeq = env.seq(bob);
auto const batchFee = batch::calcBatchFee(env, 0, 2);
auto const batchFee = batch::calcConfidentialBatchFee(env, 0, 2);
// jv1 is built against the current ledger state (spending=200).
auto const jv1 =
@@ -7215,7 +7344,7 @@ class ConfidentialTransfer_test : public beast::unit_test::Suite
setupBatchEnv(mpt2, alice2, bob2, carol2, dave2, 150, 0);
auto const bobSeq = env2.seq(bob2);
auto const batchFee = batch::calcBatchFee(env2, 0, 2);
auto const batchFee = batch::calcConfidentialBatchFee(env2, 0, 2);
auto const jv1 =
mpt2.sendJV({.account = bob2, .dest = carol2, .amt = 100}, bobSeq + 1);
@@ -7261,7 +7390,7 @@ class ConfidentialTransfer_test : public beast::unit_test::Suite
{
auto const bobSeq = env.seq(bob);
auto const carolSeq = env.seq(carol);
auto const batchFee = batch::calcBatchFee(env, 1, 2);
auto const batchFee = batch::calcConfidentialBatchFee(env, 1, 2);
auto const jv1 = mpt.sendJV({.account = bob, .dest = dave, .amt = 10}, bobSeq + 1);
auto const jv2 = mpt.sendJV({.account = carol, .dest = dave, .amt = 5}, carolSeq);
@@ -7300,7 +7429,7 @@ class ConfidentialTransfer_test : public beast::unit_test::Suite
{
auto const bobSeq = env.seq(bob);
auto const carolSeq = env.seq(carol);
auto const batchFee = batch::calcBatchFee(env, 1, 2);
auto const batchFee = batch::calcConfidentialBatchFee(env, 1, 2);
// Both proofs fail range check (amount > balance)
auto const jv1 = mpt.sendJV({.account = bob, .dest = dave, .amt = 200}, bobSeq + 1);
@@ -7323,7 +7452,7 @@ class ConfidentialTransfer_test : public beast::unit_test::Suite
{
auto const bobSeq = env.seq(bob);
auto const carolSeq = env.seq(carol);
auto const batchFee = batch::calcBatchFee(env, 1, 2);
auto const batchFee = batch::calcConfidentialBatchFee(env, 1, 2);
auto jv1 = mpt.sendJV({.account = bob, .dest = dave, .amt = 200}, bobSeq + 1);
auto jv2 = mpt.sendJV({.account = carol, .dest = dave, .amt = 5}, carolSeq);
@@ -7363,7 +7492,7 @@ class ConfidentialTransfer_test : public beast::unit_test::Suite
{
auto const bobSeq = env.seq(bob);
auto const carolSeq = env.seq(carol);
auto const batchFee = batch::calcBatchFee(env, 1, 2);
auto const batchFee = batch::calcConfidentialBatchFee(env, 1, 2);
auto const jv1 = mpt.sendJV({.account = bob, .dest = dave, .amt = 200}, bobSeq + 1);
auto const jv2 = mpt.sendJV({.account = carol, .dest = dave, .amt = 5}, carolSeq);
@@ -7383,7 +7512,7 @@ class ConfidentialTransfer_test : public beast::unit_test::Suite
{
auto const bobSeq = env.seq(bob);
auto const carolSeq = env.seq(carol);
auto const batchFee = batch::calcBatchFee(env, 1, 2);
auto const batchFee = batch::calcConfidentialBatchFee(env, 1, 2);
auto const jv1 = mpt.sendJV({.account = bob, .dest = dave, .amt = 10}, bobSeq + 1);
auto const jv2 = mpt.sendJV({.account = carol, .dest = dave, .amt = 5}, carolSeq);
@@ -7424,7 +7553,7 @@ class ConfidentialTransfer_test : public beast::unit_test::Suite
{
auto const bobSeq = env.seq(bob);
auto const carolSeq = env.seq(carol);
auto const batchFee = batch::calcBatchFee(env, 1, 3);
auto const batchFee = batch::calcConfidentialBatchFee(env, 1, 3);
auto const jv1 = mpt.sendJV({.account = bob, .dest = dave, .amt = 10}, bobSeq + 1);
@@ -7482,7 +7611,7 @@ class ConfidentialTransfer_test : public beast::unit_test::Suite
mpt.pay(alice, bob, 50);
auto const bobSeq = env.seq(bob);
auto const batchFee = batch::calcBatchFee(env, 0, 2);
auto const batchFee = batch::calcConfidentialBatchFee(env, 0, 2);
// jv1: convert 50 regular MPT into confidential inbox
auto const jv1 = mpt.convertJV({.account = bob, .amt = 50}, bobSeq + 1);
@@ -7523,7 +7652,7 @@ class ConfidentialTransfer_test : public beast::unit_test::Suite
mpt.pay(alice, bob, 50);
auto const bobSeq = env.seq(bob);
auto const batchFee = batch::calcBatchFee(env, 0, 3);
auto const batchFee = batch::calcConfidentialBatchFee(env, 0, 3);
auto const jv1 = mpt.convertJV({.account = bob, .amt = 50}, bobSeq + 1);
auto const jv2 = mpt.mergeInboxJV({.account = bob});
@@ -7586,7 +7715,7 @@ class ConfidentialTransfer_test : public beast::unit_test::Suite
auto const carolSeq = env.seq(carol);
auto const daveSeq = env.seq(dave);
// 2 extra signers (carol, dave), 4 inner txns
auto const batchFee = batch::calcBatchFee(env, 2, 4);
auto const batchFee = batch::calcConfidentialBatchFee(env, 2, 4);
// jv1: bob sends 30 to carol
auto const jv1 = mpt.sendJV({.account = bob, .dest = carol, .amt = 30}, bobSeq + 1);
@@ -7633,7 +7762,7 @@ class ConfidentialTransfer_test : public beast::unit_test::Suite
setupBatchEnv(mpt, alice, bob, carol, dave, 100, 0);
auto const bobSeq = env.seq(bob);
auto const batchFee = batch::calcBatchFee(env, 0, 2);
auto const batchFee = batch::calcConfidentialBatchFee(env, 0, 2);
// jv1: bob sends 30 to carol (spending 100->70, version V->V+1)
auto const jv1 = mpt.sendJV({.account = bob, .dest = carol, .amt = 30}, bobSeq + 1);
@@ -7679,7 +7808,7 @@ class ConfidentialTransfer_test : public beast::unit_test::Suite
auto const bobSeq = env.seq(bob);
// 0 extra signers: all inner txns are from bob;
auto const batchFee = batch::calcBatchFee(env, 0, 2);
auto const batchFee = batch::calcConfidentialBatchFee(env, 0, 2);
// When the outer uses a ticket (seq=0), inner txns start from bobSeq, bobSeq+1.
// jv2 must use chain state predicted after jv1 since both sends are from bob.
@@ -7721,7 +7850,7 @@ class ConfidentialTransfer_test : public beast::unit_test::Suite
env.close();
auto const bobSeq = env.seq(bob);
auto const batchFee = batch::calcBatchFee(env, 0, 2);
auto const batchFee = batch::calcConfidentialBatchFee(env, 0, 2);
// jv1: proof bound to ticketSeq1.
auto const jv1 = mpt.sendJV({.account = bob, .dest = carol, .amt = 40}, ticketSeq1);
@@ -7758,7 +7887,7 @@ class ConfidentialTransfer_test : public beast::unit_test::Suite
env.close();
auto const bobSeq = env.seq(bob);
auto const batchFee = batch::calcBatchFee(env, 0, 2);
auto const batchFee = batch::calcConfidentialBatchFee(env, 0, 2);
// Proof intentionally built with account seq (bobSeq+1) instead of ticketSeq.
auto const badJV = mpt.sendJV({.account = bob, .dest = carol, .amt = 40}, bobSeq + 1);
@@ -8501,7 +8630,8 @@ class ConfidentialTransfer_test : public beast::unit_test::Suite
ConfidentialEnv confEnv{
env,
alice,
{{.account = bob, .payAmount = 1000, .convertAmount = 10}, {carol, 1000, 50}}};
{{.account = bob, .payAmount = 1000, .convertAmount = 10},
{.account = carol, .payAmount = 1000, .convertAmount = 50}}};
auto& mptAlice = confEnv.mpt;
uint64_t const sendAmount = 10;
@@ -8683,7 +8813,7 @@ class ConfidentialTransfer_test : public beast::unit_test::Suite
alice,
{{.account = bob},
{.account = carol, .payAmount = 1000, .convertAmount = 50},
{dan, 1000, 50}}};
{.account = dan, .payAmount = 1000, .convertAmount = 50}}};
auto& mptAlice = confEnv.mpt;
uint64_t const sendAmount = 10;
@@ -9768,6 +9898,9 @@ class ConfidentialTransfer_test : public beast::unit_test::Suite
// Crafted-proof Tests
testSendSharedRandomnessViolation(features);
// Fee Tests
testConfidentialMPTBaseFee(features);
// Ticket Tests
testWithTickets(features);
testConvertTicketProofBinding(features);

View File

@@ -21,6 +21,10 @@ namespace xrpl::test::jtx::batch {
XRPAmount
calcBatchFee(jtx::Env const& env, uint32_t const& numSigners, uint32_t const& txns = 0);
/** Calculate Batch Fee with Confidential MPT inner transactions. */
XRPAmount
calcConfidentialBatchFee(jtx::Env const& env, uint32_t const& numSigners, uint32_t const& txns = 0);
/** Batch. */
json::Value
outer(jtx::Account const& account, uint32_t seq, STAmount const& fee, std::uint32_t flags);

View File

@@ -37,6 +37,16 @@ calcBatchFee(test::jtx::Env const& env, uint32_t const& numSigners, uint32_t con
return ((numSigners + 2) * feeDrops) + feeDrops * txns;
}
XRPAmount
calcConfidentialBatchFee(
test::jtx::Env const& env,
uint32_t const& numSigners,
uint32_t const& txns)
{
XRPAmount const feeDrops = env.current()->fees().base;
return ((numSigners + 2) * feeDrops) + feeDrops * 10 * txns;
}
// Batch.
json::Value
outer(jtx::Account const& account, uint32_t seq, STAmount const& fee, std::uint32_t flags)

View File

@@ -57,7 +57,22 @@ fillFee(json::Value& jv, ReadView const& view)
{
if (jv.isMember(jss::Fee))
return;
jv[jss::Fee] = to_string(view.fees().base);
auto const base = view.fees().base;
// For confidential transactions, the fee higher because confidential
// transaction processing is more expensive.
auto const txType = jv[jss::TransactionType].asString();
if (txType == jss::ConfidentialMPTConvert || txType == jss::ConfidentialMPTConvertBack ||
txType == jss::ConfidentialMPTSend || txType == jss::ConfidentialMPTMergeInbox ||
txType == jss::ConfidentialMPTClawback)
{
jv[jss::Fee] = to_string(base * 10);
}
else
{
jv[jss::Fee] = to_string(base);
}
}
void

View File

@@ -12,6 +12,7 @@
#include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/protocol/UintTypes.h>
#include <xrpl/protocol/XRPAmount.h>
#include <cstdint>
@@ -208,6 +209,7 @@ struct MPTConvert
std::optional<std::uint32_t> ownerCount = std::nullopt;
std::optional<std::uint32_t> holderCount = std::nullopt;
std::optional<std::uint32_t> flags = std::nullopt;
std::optional<XRPAmount> fee = std::nullopt;
std::optional<TER> err = std::nullopt;
};
@@ -220,6 +222,7 @@ struct MPTMergeInbox
std::optional<std::uint32_t> ownerCount = std::nullopt;
std::optional<std::uint32_t> holderCount = std::nullopt;
std::optional<std::uint32_t> flags = std::nullopt;
std::optional<XRPAmount> fee = std::nullopt;
std::optional<TER> err = std::nullopt;
};
@@ -247,6 +250,7 @@ struct MPTConfidentialSend
std::optional<std::uint32_t> ownerCount = std::nullopt;
std::optional<std::uint32_t> holderCount = std::nullopt;
std::optional<std::uint32_t> flags = std::nullopt;
std::optional<XRPAmount> fee = std::nullopt;
std::optional<TER> err = std::nullopt;
};
@@ -268,6 +272,7 @@ struct MPTConvertBack
std::optional<std::uint32_t> ownerCount = std::nullopt;
std::optional<std::uint32_t> holderCount = std::nullopt;
std::optional<std::uint32_t> flags = std::nullopt;
std::optional<XRPAmount> fee = std::nullopt;
std::optional<TER> err = std::nullopt;
};
@@ -283,6 +288,7 @@ struct MPTConfidentialClawback
std::optional<std::uint32_t> ownerCount = std::nullopt;
std::optional<std::uint32_t> holderCount = std::nullopt;
std::optional<std::uint32_t> flags = std::nullopt;
std::optional<XRPAmount> fee = std::nullopt;
std::optional<TER> err = std::nullopt;
};
@@ -599,11 +605,17 @@ private:
template <typename A>
TER
submit(A const& arg, json::Value const& jv)
submit(A const& arg, json::Value jv)
{
auto const expectedFlags = Txflags(arg.flags.value_or(0));
auto const expectedTer = Ter(arg.err.value_or(tesSUCCESS));
if constexpr (requires { arg.fee; })
{
if (arg.fee)
jv[jss::Fee] = to_string(*arg.fee);
}
std::optional<std::uint32_t> ticketSeq;
if constexpr (requires { arg.ticketSeq; })
ticketSeq = arg.ticketSeq;