diff --git a/src/libxrpl/ledger/Ledger.cpp b/src/libxrpl/ledger/Ledger.cpp index 86a22eba60..9f70e89f02 100644 --- a/src/libxrpl/ledger/Ledger.cpp +++ b/src/libxrpl/ledger/Ledger.cpp @@ -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); } diff --git a/src/test/app/FeeVote_test.cpp b/src/test/app/FeeVote_test.cpp index 5136ef0c3b..8fd91683a1 100644 --- a/src/test/app/FeeVote_test.cpp +++ b/src/test/app/FeeVote_test.cpp @@ -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 const& amendments) { + return std::make_shared( + 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(); } }; diff --git a/src/xrpld/core/Config.h b/src/xrpld/core/Config.h index 852a9d9f4b..01d0514b0e 100644 --- a/src/xrpld/core/Config.h +++ b/src/xrpld/core/Config.h @@ -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; } };