feature BatchV1_1

This commit is contained in:
Denis Angell
2026-02-20 22:04:43 +01:00
parent bdd106d992
commit 69084a6ff5
9 changed files with 80 additions and 64 deletions

View File

@@ -141,14 +141,11 @@ class Batch_test : public beast::unit_test::suite
using namespace test::jtx;
using namespace std::literals;
bool const withInnerSigFix = features[fixBatchInnerSigs];
for (bool const withBatch : {true, false})
{
testcase << "enabled: Batch " << (withBatch ? "enabled" : "disabled")
<< ", Inner Sig Fix: " << (withInnerSigFix ? "enabled" : "disabled");
testcase << "enabled: Batch " << (withBatch ? "enabled" : "disabled");
auto const amend = withBatch ? features : features - featureBatch;
auto const amend = withBatch ? features : features - featureBatchV1_1;
test::jtx::Env env{*this, amend};
@@ -2191,22 +2188,16 @@ class Batch_test : public beast::unit_test::suite
void
doTestInnerSubmitRPC(FeatureBitset features, bool withBatch)
{
bool const withInnerSigFix = features[fixBatchInnerSigs];
std::string const testName =
std::string("inner submit rpc: batch ") + (withBatch ? "enabled" : "disabled") + ": ";
std::string const testName = [&]() {
std::stringstream ss;
ss << "inner submit rpc: batch " << (withBatch ? "enabled" : "disabled")
<< ", inner sig fix: " << (withInnerSigFix ? "enabled" : "disabled") << ": ";
return ss.str();
}();
auto const amend = withBatch ? features : features - featureBatch;
auto const amend = withBatch ? features : features - featureBatchV1_1;
using namespace test::jtx;
using namespace std::literals;
test::jtx::Env env{*this, amend};
if (!BEAST_EXPECT(amend[featureBatch] == withBatch))
if (!BEAST_EXPECT(amend[featureBatchV1_1] == withBatch))
return;
auto const alice = Account("alice");
@@ -2328,8 +2319,7 @@ class Batch_test : public beast::unit_test::suite
s.slice(),
__LINE__,
"fails local checks: Empty SigningPubKey.",
"fails local checks: Empty SigningPubKey.",
withBatch && !withInnerSigFix);
"fails local checks: Empty SigningPubKey.");
}
// Invalid RPC Submission: tfInnerBatchTxn pseudo-transaction
@@ -2340,7 +2330,7 @@ class Batch_test : public beast::unit_test::suite
{
STTx amendTx(ttAMENDMENT, [seq = env.closed()->header().seq + 1](auto& obj) {
obj.setAccountID(sfAccount, AccountID());
obj.setFieldH256(sfAmendment, fixBatchInnerSigs);
obj.setFieldH256(sfAmendment, featureBatchV1_1);
obj.setFieldU32(sfLedgerSequence, seq);
obj.setFieldU32(sfFlags, tfInnerBatchTxn);
});
@@ -2352,8 +2342,7 @@ class Batch_test : public beast::unit_test::suite
"Pseudo-transaction",
s.slice(),
__LINE__,
withInnerSigFix ? "fails local checks: Empty SigningPubKey."
: "fails local checks: Cannot submit pseudo transactions.",
"fails local checks: Empty SigningPubKey.",
"fails local checks: Empty SigningPubKey.");
}
}
@@ -2414,6 +2403,53 @@ class Batch_test : public beast::unit_test::suite
BEAST_EXPECT(env.balance(bob) == XRP(1000));
}
void
testCheckAllSignatures(FeatureBitset features)
{
testcase("check all signatures");
using namespace test::jtx;
using namespace std::literals;
// Verifies that checkBatchSign validates all signers even when an
// unfunded account (signed with its master key) appears first in the
// sorted signer list. A funded account with an invalid signature must
// still be rejected with tefBAD_AUTH.
test::jtx::Env env{*this, features};
auto const alice = Account("alice");
// "aaa" sorts before other accounts alphabetically, ensuring the
// unfunded account is checked first in the sorted signer list
auto const unfunded = Account("aaa");
auto const carol = Account("carol");
env.fund(XRP(10000), alice, carol);
env.close();
// Verify sort order: unfunded.id() < carol.id()
BEAST_EXPECT(unfunded.id() < carol.id());
auto const seq = env.seq(alice);
auto const ledSeq = env.current()->seq();
auto const batchFee = batch::calcBatchFee(env, 2, 3);
// The batch includes:
// 1. alice pays unfunded (to create unfunded's account)
// 2. unfunded does a noop (signed by unfunded's master key - valid)
// 3. carol pays alice (signed by alice's key - INVALID since alice is
// not carol's regular key)
//
// checkBatchSign must validate all signers regardless of order.
// This must fail with tefBAD_AUTH.
env(batch::outer(alice, seq, batchFee, tfAllOrNothing),
batch::inner(pay(alice, unfunded, XRP(100)), seq + 1),
batch::inner(noop(unfunded), ledSeq),
batch::inner(pay(carol, alice, XRP(1000)), env.seq(carol)),
batch::sig(unfunded, Reg{carol, alice}),
ter(tefBAD_AUTH));
env.close();
}
void
testAccountSet(FeatureBitset features)
{
@@ -4331,6 +4367,7 @@ class Batch_test : public beast::unit_test::suite
testIndependent(features);
testInnerSubmitRPC(features);
testAccountActivation(features);
testCheckAllSignatures(features);
testAccountSet(features);
testAccountDelete(features);
testLoan(features);
@@ -4356,7 +4393,6 @@ public:
{
using namespace test::jtx;
auto const sa = testable_amendments();
testWithFeats(sa - fixBatchInnerSigs);
testWithFeats(sa);
}
};

View File

@@ -114,9 +114,8 @@ class Feature_test : public beast::unit_test::suite
// Test a random sampling of the variables. If any of these get retired
// or removed, swap out for any other feature.
BEAST_EXPECT(
featureToName(fixRemoveNFTokenAutoTrustLine) == "fixRemoveNFTokenAutoTrustLine");
BEAST_EXPECT(featureToName(featureBatch) == "Batch");
BEAST_EXPECT(featureToName(fixRemoveNFTokenAutoTrustLine) == "fixRemoveNFTokenAutoTrustLine");
BEAST_EXPECT(featureToName(featureBatchV1_1) == "BatchV1_1");
BEAST_EXPECT(featureToName(featureDID) == "DID");
BEAST_EXPECT(featureToName(fixIncludeKeyletFields) == "fixIncludeKeyletFields");
BEAST_EXPECT(featureToName(featureTokenEscrow) == "TokenEscrow");