mirror of
https://github.com/XRPLF/rippled.git
synced 2026-09-27 23:38:08 +00:00
fix: Carry the configured gas settings into the genesis ledger
`FeeSetup::toFees()` returned the three-argument `Fees`, leaving `gasLimit`, `bytecodeSizeLimit` and `gasPrice` at zero. `startGenesisLedger` passes that straight into the genesis `Ledger`, which is where those three are written to the `FeeSettings` entry — so a network started fresh with `featureSmartEscrow` in its initial amendment set got a gas limit of zero, i.e. Smart Escrow switched off no matter what the operator configured. Carrying them through `toFees()` alone is not enough. Every `Ledger` built from stored data is seeded with the same `Fees`, and `Ledger::setup()` only overwrites the fields the `FeeSettings` entry actually carries. A ledger from before the amendment carries none, so the seed would survive and the node would read its own configuration back as though the network had agreed to it — and then never vote for the values it wants, because `doValidation` only votes when the current setting differs from the target. So the genesis constructor now clears the three whenever it does not write them. A ledger reports what its `FeeSettings` entry holds, and nothing more. Reported by depthfirst-app and Copilot on #8214.
This commit is contained in:
@@ -204,6 +204,16 @@ Ledger::Ledger(
|
||||
sle->at(sfBytecodeSizeLimit) = fees.bytecodeSizeLimit;
|
||||
sle->at(sfGasPrice) = fees.gasPrice;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This ledger does not carry the gas settings, so it must not
|
||||
// report them either. Otherwise a node reads its own config back
|
||||
// as though the network had agreed to it, and never votes for the
|
||||
// values it wants.
|
||||
fees_.gasLimit = 0;
|
||||
fees_.bytecodeSizeLimit = 0;
|
||||
fees_.gasPrice = 0;
|
||||
}
|
||||
rawInsert(sle);
|
||||
}
|
||||
|
||||
|
||||
@@ -935,6 +935,59 @@ class FeeVote_test : public beast::unit_test::Suite
|
||||
feeTx.getFieldAmount(sfReserveIncrementDrops) == XRPAmount{setup.ownerReserve});
|
||||
}
|
||||
|
||||
void
|
||||
testGenesisFeeSettings()
|
||||
{
|
||||
testcase("genesis FeeSettings carries the configured gas settings");
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
Env env(*this, testableAmendments());
|
||||
auto const& cfg = env.app().config().fees;
|
||||
BEAST_EXPECT(cfg.gasLimit != 0 && cfg.bytecodeSizeLimit != 0 && cfg.gasPrice != 0);
|
||||
|
||||
auto const genesisWith = [&](std::vector<uint256> const& amendments) {
|
||||
return std::make_shared<Ledger>(
|
||||
kCreateGenesis,
|
||||
Rules{env.app().config().features},
|
||||
cfg.toFees(),
|
||||
amendments,
|
||||
env.app().getNodeFamily());
|
||||
};
|
||||
|
||||
{
|
||||
// With the amendment, the operator's configuration becomes the
|
||||
// network's starting point.
|
||||
auto const ledger = genesisWith({featureSmartEscrow});
|
||||
auto const sle = ledger->read(keylet::feeSettings());
|
||||
if (BEAST_EXPECT(sle))
|
||||
{
|
||||
BEAST_EXPECT(sle->getFieldU32(sfGasLimit) == cfg.gasLimit);
|
||||
BEAST_EXPECT(sle->getFieldU32(sfBytecodeSizeLimit) == cfg.bytecodeSizeLimit);
|
||||
BEAST_EXPECT(sle->getFieldU32(sfGasPrice) == cfg.gasPrice);
|
||||
}
|
||||
BEAST_EXPECT(ledger->fees().gasLimit == cfg.gasLimit);
|
||||
BEAST_EXPECT(ledger->fees().bytecodeSizeLimit == cfg.bytecodeSizeLimit);
|
||||
BEAST_EXPECT(ledger->fees().gasPrice == cfg.gasPrice);
|
||||
}
|
||||
|
||||
{
|
||||
// Without it, the entry carries nothing and the ledger reports
|
||||
// nothing, so the node still has something to vote for.
|
||||
auto const ledger = genesisWith({});
|
||||
auto const sle = ledger->read(keylet::feeSettings());
|
||||
if (BEAST_EXPECT(sle))
|
||||
{
|
||||
BEAST_EXPECT(!sle->isFieldPresent(sfGasLimit));
|
||||
BEAST_EXPECT(!sle->isFieldPresent(sfBytecodeSizeLimit));
|
||||
BEAST_EXPECT(!sle->isFieldPresent(sfGasPrice));
|
||||
}
|
||||
BEAST_EXPECT(ledger->fees().gasLimit == 0);
|
||||
BEAST_EXPECT(ledger->fees().bytecodeSizeLimit == 0);
|
||||
BEAST_EXPECT(ledger->fees().gasPrice == 0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testDoVotingSmartEscrow()
|
||||
{
|
||||
@@ -1098,6 +1151,7 @@ class FeeVote_test : public beast::unit_test::Suite
|
||||
testSingleInvalidTransaction();
|
||||
testDoValidation();
|
||||
testDoVoting();
|
||||
testGenesisFeeSettings();
|
||||
testDoVotingSmartEscrow();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -90,7 +90,11 @@ struct FeeSetup
|
||||
[[nodiscard]] Fees
|
||||
toFees() const
|
||||
{
|
||||
return Fees{referenceFee, accountReserve, ownerReserve};
|
||||
Fees fees{referenceFee, accountReserve, ownerReserve};
|
||||
fees.gasLimit = gasLimit;
|
||||
fees.bytecodeSizeLimit = bytecodeSizeLimit;
|
||||
fees.gasPrice = gasPrice;
|
||||
return fees;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user