mirror of
https://github.com/Xahau/xahaud.git
synced 2026-04-29 15:37:46 +00:00
Add gas price
This commit is contained in:
@@ -109,6 +109,7 @@
|
||||
#define sfMPTAmount ((3U << 16U) + 26U)
|
||||
#define sfIssuerNode ((3U << 16U) + 27U)
|
||||
#define sfSubjectNode ((3U << 16U) + 28U)
|
||||
#define sfHookGasPrice ((3U << 16U) + 96U)
|
||||
#define sfTouchCount ((3U << 16U) + 97U)
|
||||
#define sfAccountIndex ((3U << 16U) + 98U)
|
||||
#define sfAccountCount ((3U << 16U) + 99U)
|
||||
|
||||
@@ -34,6 +34,8 @@ struct Fees
|
||||
XRPAmount base{0}; // Reference tx cost (drops)
|
||||
XRPAmount reserve{0}; // Reserve base (drops)
|
||||
XRPAmount increment{0}; // Reserve increment (drops)
|
||||
std::uint64_t hookGasPrice{
|
||||
0}; // Gas price for gas-type hooks (micro-drops per gas unit)
|
||||
|
||||
explicit Fees() = default;
|
||||
Fees(Fees const&) = default;
|
||||
|
||||
@@ -416,6 +416,7 @@ LEDGER_ENTRY(ltFEE_SETTINGS, 0x0073, FeeSettings, fee, ({
|
||||
{sfBaseFeeDrops, soeOPTIONAL},
|
||||
{sfReserveBaseDrops, soeOPTIONAL},
|
||||
{sfReserveIncrementDrops, soeOPTIONAL},
|
||||
{sfHookGasPrice, soeOPTIONAL},
|
||||
{sfXahauActivationLgrSeq, soeOPTIONAL},
|
||||
{sfAccountCount, soeOPTIONAL},
|
||||
{sfNetworkID, soeOPTIONAL},
|
||||
|
||||
@@ -157,6 +157,7 @@ TYPED_SFIELD(sfOutstandingAmount, UINT64, 25, SField::sMD_BaseTen|SFie
|
||||
TYPED_SFIELD(sfMPTAmount, UINT64, 26, SField::sMD_BaseTen|SField::sMD_Default)
|
||||
TYPED_SFIELD(sfIssuerNode, UINT64, 27)
|
||||
TYPED_SFIELD(sfSubjectNode, UINT64, 28)
|
||||
TYPED_SFIELD(sfHookGasPrice, UINT64, 96)
|
||||
TYPED_SFIELD(sfTouchCount, UINT64, 97)
|
||||
TYPED_SFIELD(sfAccountIndex, UINT64, 98)
|
||||
TYPED_SFIELD(sfAccountCount, UINT64, 99)
|
||||
|
||||
@@ -583,6 +583,7 @@ TRANSACTION(ttFEE, 101, SetFee, ({
|
||||
{sfBaseFeeDrops, soeOPTIONAL},
|
||||
{sfReserveBaseDrops, soeOPTIONAL},
|
||||
{sfReserveIncrementDrops, soeOPTIONAL},
|
||||
{sfHookGasPrice, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
/** This system-generated transaction type is used to update the network's negative UNL
|
||||
|
||||
@@ -66,6 +66,8 @@ STValidation::validationFormat()
|
||||
{sfBaseFeeDrops, soeOPTIONAL},
|
||||
{sfReserveBaseDrops, soeOPTIONAL},
|
||||
{sfReserveIncrementDrops, soeOPTIONAL},
|
||||
// featureHookGas
|
||||
{sfHookGasPrice, soeOPTIONAL},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ class FeeVote_test : public beast::unit_test::suite
|
||||
void
|
||||
testSetup()
|
||||
{
|
||||
testcase("setup");
|
||||
FeeSetup const defaultSetup;
|
||||
{
|
||||
// defaults
|
||||
@@ -37,36 +38,42 @@ class FeeVote_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(setup.reference_fee == defaultSetup.reference_fee);
|
||||
BEAST_EXPECT(setup.account_reserve == defaultSetup.account_reserve);
|
||||
BEAST_EXPECT(setup.owner_reserve == defaultSetup.owner_reserve);
|
||||
BEAST_EXPECT(setup.hook_gas_price == defaultSetup.hook_gas_price);
|
||||
}
|
||||
{
|
||||
Section config;
|
||||
config.append(
|
||||
{"reference_fee = 50",
|
||||
"account_reserve = 1234567",
|
||||
"owner_reserve = 1234"});
|
||||
"owner_reserve = 1234",
|
||||
"hook_gas_price = 2000000"});
|
||||
auto setup = setup_FeeVote(config);
|
||||
BEAST_EXPECT(setup.reference_fee == 50);
|
||||
BEAST_EXPECT(setup.account_reserve == 1234567);
|
||||
BEAST_EXPECT(setup.owner_reserve == 1234);
|
||||
BEAST_EXPECT(setup.hook_gas_price == 2'000'000);
|
||||
}
|
||||
{
|
||||
Section config;
|
||||
config.append(
|
||||
{"reference_fee = blah",
|
||||
"account_reserve = yada",
|
||||
"owner_reserve = foo"});
|
||||
"owner_reserve = foo",
|
||||
"hook_gas_price =invalid"});
|
||||
// Illegal values are ignored, and the defaults left unchanged
|
||||
auto setup = setup_FeeVote(config);
|
||||
BEAST_EXPECT(setup.reference_fee == defaultSetup.reference_fee);
|
||||
BEAST_EXPECT(setup.account_reserve == defaultSetup.account_reserve);
|
||||
BEAST_EXPECT(setup.owner_reserve == defaultSetup.owner_reserve);
|
||||
BEAST_EXPECT(setup.hook_gas_price == defaultSetup.hook_gas_price);
|
||||
}
|
||||
{
|
||||
Section config;
|
||||
config.append(
|
||||
{"reference_fee = -50",
|
||||
"account_reserve = -1234567",
|
||||
"owner_reserve = -1234"});
|
||||
"owner_reserve = -1234",
|
||||
"hook_gas_price = -2000000"});
|
||||
// Illegal values are ignored, and the defaults left unchanged
|
||||
auto setup = setup_FeeVote(config);
|
||||
BEAST_EXPECT(setup.reference_fee == defaultSetup.reference_fee);
|
||||
@@ -74,22 +81,30 @@ class FeeVote_test : public beast::unit_test::suite
|
||||
setup.account_reserve == static_cast<std::uint32_t>(-1234567));
|
||||
BEAST_EXPECT(
|
||||
setup.owner_reserve == static_cast<std::uint32_t>(-1234));
|
||||
BEAST_EXPECT(
|
||||
setup.hook_gas_price == static_cast<std::int64_t>(-2000000));
|
||||
}
|
||||
{
|
||||
const auto big64 = std::to_string(
|
||||
const auto big64xrp = std::to_string(
|
||||
static_cast<std::uint64_t>(
|
||||
std::numeric_limits<XRPAmount::value_type>::max()) +
|
||||
1);
|
||||
const auto big64 = std::to_string(
|
||||
static_cast<std::uint64_t>(
|
||||
std::numeric_limits<std::int64_t>::max()) +
|
||||
1);
|
||||
Section config;
|
||||
config.append(
|
||||
{"reference_fee = " + big64,
|
||||
"account_reserve = " + big64,
|
||||
"owner_reserve = " + big64});
|
||||
{"reference_fee = " + big64xrp,
|
||||
"account_reserve = " + big64xrp,
|
||||
"owner_reserve = " + big64xrp,
|
||||
"hook_gas_price =" + big64});
|
||||
// Illegal values are ignored, and the defaults left unchanged
|
||||
auto setup = setup_FeeVote(config);
|
||||
BEAST_EXPECT(setup.reference_fee == defaultSetup.reference_fee);
|
||||
BEAST_EXPECT(setup.account_reserve == defaultSetup.account_reserve);
|
||||
BEAST_EXPECT(setup.owner_reserve == defaultSetup.owner_reserve);
|
||||
BEAST_EXPECT(setup.hook_gas_price == defaultSetup.hook_gas_price);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
260
src/test/app/HookGasPrice_test.cpp
Normal file
260
src/test/app/HookGasPrice_test.cpp
Normal file
@@ -0,0 +1,260 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2025 XRPL Labs
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <test/jtx.h>
|
||||
#include <xrpld/app/misc/FeeVote.h>
|
||||
#include <xrpld/app/tx/detail/Transactor.h>
|
||||
#include <xrpld/core/Config.h>
|
||||
#include <xrpl/basics/BasicConfig.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Fees.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
class HookGasPrice_test : public beast::unit_test::suite
|
||||
{
|
||||
// Advance to flag ledger so fee voting sets sfHookGasPrice
|
||||
void
|
||||
advanceToFlagLedger(jtx::Env& env)
|
||||
{
|
||||
auto const seq = env.current()->info().seq;
|
||||
for (auto i = seq; i <= 256; ++i)
|
||||
env.close();
|
||||
env.close();
|
||||
}
|
||||
|
||||
void
|
||||
testHookGasPriceGenesis(FeatureBitset features)
|
||||
{
|
||||
testcase("GasPrice genesis");
|
||||
using namespace jtx;
|
||||
|
||||
// Test Env passes empty amendments to genesis, so sfHookGasPrice
|
||||
// is NOT set in the genesis FeeSettings SLE.
|
||||
{
|
||||
Env env{*this, features};
|
||||
auto const sle = env.le(keylet::fees());
|
||||
if (!BEAST_EXPECT(sle))
|
||||
return;
|
||||
BEAST_EXPECT(!sle->isFieldPresent(sfHookGasPrice));
|
||||
}
|
||||
|
||||
// Without featureHookGas - also no sfHookGasPrice
|
||||
{
|
||||
Env env{*this, features - featureHookGas};
|
||||
auto const sle = env.le(keylet::fees());
|
||||
if (!BEAST_EXPECT(sle))
|
||||
return;
|
||||
BEAST_EXPECT(!sle->isFieldPresent(sfHookGasPrice));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testHookGasPriceFeeVoting(FeatureBitset features)
|
||||
{
|
||||
testcase("GasPrice fee voting");
|
||||
using namespace jtx;
|
||||
|
||||
Env env{*this, features};
|
||||
env.fund(XRP(10000), Account{"alice"});
|
||||
env.close();
|
||||
|
||||
// Before flag ledger: sfHookGasPrice not in SLE
|
||||
{
|
||||
auto const sle = env.le(keylet::fees());
|
||||
if (!BEAST_EXPECT(sle))
|
||||
return;
|
||||
BEAST_EXPECT(!sle->isFieldPresent(sfHookGasPrice));
|
||||
}
|
||||
|
||||
// Advance to flag ledger to trigger fee voting
|
||||
advanceToFlagLedger(env);
|
||||
|
||||
// After fee voting with featureHookGas enabled:
|
||||
// sfHookGasPrice should be set in SLE via ttFEE pseudo-transaction
|
||||
{
|
||||
auto const sle = env.le(keylet::fees());
|
||||
if (!BEAST_EXPECT(sle))
|
||||
return;
|
||||
BEAST_EXPECT(sle->isFieldPresent(sfHookGasPrice));
|
||||
if (sle->isFieldPresent(sfHookGasPrice))
|
||||
BEAST_EXPECT(sle->getFieldU64(sfHookGasPrice) == 1'000'000);
|
||||
}
|
||||
BEAST_EXPECT(
|
||||
env.current()->fees().hookGasPrice == XRPAmount{1'000'000});
|
||||
}
|
||||
|
||||
void
|
||||
testHookGasPriceVotingUpdate(FeatureBitset features)
|
||||
{
|
||||
testcase("GasPrice voting update");
|
||||
using namespace jtx;
|
||||
|
||||
// Create env with custom gas_price = 2,000,000 via voting config
|
||||
auto cfg = envconfig();
|
||||
auto& votingSection = cfg->section("voting");
|
||||
votingSection.set("hook_gas_price", "2000000");
|
||||
// A validation_seed is required for fee voting to work
|
||||
cfg->section("validation_seed").legacy("shUwVw52ofnCUX5m7kPTKzJdr4HEH");
|
||||
|
||||
Env env{*this, std::move(cfg), features};
|
||||
env.fund(XRP(10000), Account{"alice"});
|
||||
env.close();
|
||||
|
||||
// Before flag ledger: sfHookGasPrice not in SLE
|
||||
{
|
||||
auto const sle = env.le(keylet::fees());
|
||||
if (!BEAST_EXPECT(sle))
|
||||
return;
|
||||
BEAST_EXPECT(!sle->isFieldPresent(sfHookGasPrice));
|
||||
}
|
||||
|
||||
// Advance to flag ledger to trigger fee voting
|
||||
advanceToFlagLedger(env);
|
||||
|
||||
// After voting: sfHookGasPrice should be updated to 2,000,000
|
||||
{
|
||||
auto const sle = env.le(keylet::fees());
|
||||
if (!BEAST_EXPECT(sle))
|
||||
return;
|
||||
BEAST_EXPECT(sle->isFieldPresent(sfHookGasPrice));
|
||||
if (sle->isFieldPresent(sfHookGasPrice))
|
||||
BEAST_EXPECT(sle->getFieldU64(sfHookGasPrice) == 2'000'000);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testHookGasPriceFeeCalculation(FeatureBitset features)
|
||||
{
|
||||
testcase("GasPrice fee calculation");
|
||||
using namespace jtx;
|
||||
|
||||
{
|
||||
// With default gasPrice = 1,000,000:
|
||||
auto cfg = envconfig();
|
||||
auto& votingSection = cfg->section("voting");
|
||||
votingSection.set("hook_gas_price", "1000000");
|
||||
// A validation_seed is required for fee voting to work
|
||||
cfg->section("validation_seed")
|
||||
.legacy("shUwVw52ofnCUX5m7kPTKzJdr4HEH");
|
||||
|
||||
Env env{*this, std::move(cfg), features};
|
||||
env.fund(XRP(10000), Account{"alice"});
|
||||
env.close();
|
||||
|
||||
// Advance to flag ledger so gasPrice is set via fee voting
|
||||
advanceToFlagLedger(env);
|
||||
|
||||
auto const& fees = env.current()->fees();
|
||||
BEAST_EXPECT(fees.hookGasPrice == XRPAmount{1'000'000});
|
||||
|
||||
// Verify the gas price calculation formula:
|
||||
// fee = gasCount * gasPrice / 1,000,000
|
||||
|
||||
// With default gasPrice = 1,000,000:
|
||||
// 1,000,000 gas => 1,000,000 * 1,000,000 / 1,000,000 = 1,000,000
|
||||
// 500,000 gas => 500,000 drops
|
||||
// 0 gas => 0 drops
|
||||
for (auto gasCount : {1'000'000, 500'000, 0})
|
||||
{
|
||||
auto gasFee = Transactor::calculateHookGas(gasCount, fees);
|
||||
BEAST_EXPECT(gasFee == XRPAmount{gasCount});
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// With gasPrice = 100,000:
|
||||
auto cfg = envconfig();
|
||||
auto& votingSection = cfg->section("voting");
|
||||
votingSection.set("hook_gas_price", "100000");
|
||||
// A validation_seed is required for fee voting to work
|
||||
cfg->section("validation_seed")
|
||||
.legacy("shUwVw52ofnCUX5m7kPTKzJdr4HEH");
|
||||
|
||||
Env env{*this, std::move(cfg), features};
|
||||
env.fund(XRP(10000), Account{"alice"});
|
||||
env.close();
|
||||
|
||||
// Advance to flag ledger so gasPrice is set via fee voting
|
||||
advanceToFlagLedger(env);
|
||||
|
||||
auto const& fees = env.current()->fees();
|
||||
BEAST_EXPECT(fees.hookGasPrice == XRPAmount{100'000});
|
||||
|
||||
// Verify the gas price calculation formula:
|
||||
// fee = gasCount * gasPrice / 1,000,000
|
||||
|
||||
// With default gasPrice = 1,000,000:
|
||||
// 1,000,000 gas => 1,000,000 * 100,000 / 1,000,000 = 100,000
|
||||
// 500,000 gas => 50,000 drops
|
||||
// 0 gas => 0 drops
|
||||
for (auto gasCount : {1'000'000, 500'000, 0})
|
||||
{
|
||||
auto gasFee = Transactor::calculateHookGas(gasCount, fees);
|
||||
BEAST_EXPECT(gasFee == XRPAmount{gasCount / 10});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testHookGasPriceDisabled(FeatureBitset features)
|
||||
{
|
||||
testcase("GasPrice disabled");
|
||||
using namespace jtx;
|
||||
|
||||
Env env{*this, features - featureHookGas};
|
||||
env.fund(XRP(10000), Account{"alice"});
|
||||
env.close();
|
||||
|
||||
// sfHookGasPrice not in SLE when amendment disabled
|
||||
auto const sle = env.le(keylet::fees());
|
||||
if (!BEAST_EXPECT(sle))
|
||||
return;
|
||||
BEAST_EXPECT(!sle->isFieldPresent(sfHookGasPrice));
|
||||
|
||||
// Advance past flag ledger - sfHookGasPrice should still not be set
|
||||
advanceToFlagLedger(env);
|
||||
|
||||
{
|
||||
auto const sle2 = env.le(keylet::fees());
|
||||
if (!BEAST_EXPECT(sle2))
|
||||
return;
|
||||
BEAST_EXPECT(!sle2->isFieldPresent(sfHookGasPrice));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
using namespace jtx;
|
||||
auto const sa = supported_amendments();
|
||||
testHookGasPriceGenesis(sa);
|
||||
testHookGasPriceFeeVoting(sa);
|
||||
testHookGasPriceVotingUpdate(sa);
|
||||
testHookGasPriceFeeCalculation(sa);
|
||||
testHookGasPriceDisabled(sa);
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(HookGasPrice, app, ripple);
|
||||
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
@@ -244,6 +244,12 @@ Ledger::Ledger(
|
||||
sle->at(sfReserveIncrement) = *f;
|
||||
sle->at(sfReferenceFeeUnits) = Config::FEE_UNITS_DEPRECATED;
|
||||
}
|
||||
if (std::find(amendments.begin(), amendments.end(), featureHookGas) !=
|
||||
amendments.end())
|
||||
{
|
||||
if (auto const f = config.FEES.hook_gas_price)
|
||||
sle->at(sfHookGasPrice) = f;
|
||||
}
|
||||
rawInsert(sle);
|
||||
}
|
||||
|
||||
@@ -695,12 +701,22 @@ Ledger::setup()
|
||||
assign(fees_.increment, reserveIncrementXRP);
|
||||
newFees = baseFeeXRP || reserveBaseXRP || reserveIncrementXRP;
|
||||
}
|
||||
// Read GasPrice
|
||||
bool hookFees = false;
|
||||
if (auto const gp = sle->at(~sfHookGasPrice))
|
||||
{
|
||||
fees_.hookGasPrice = *gp;
|
||||
hookFees = true;
|
||||
}
|
||||
if (oldFees && newFees)
|
||||
// Should be all of one or the other, but not both
|
||||
ret = false;
|
||||
if (!rules_.enabled(featureXRPFees) && newFees)
|
||||
// Can't populate the new fees before the amendment is enabled
|
||||
ret = false;
|
||||
if (!rules_.enabled(featureHookGas) && hookFees)
|
||||
// Can't populate hook gas price before the amendment is enabled
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
catch (SHAMapMissingNode const&)
|
||||
@@ -720,7 +736,8 @@ void
|
||||
Ledger::defaultFees(Config const& config)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
fees_.base == 0 && fees_.reserve == 0 && fees_.increment == 0,
|
||||
fees_.base == 0 && fees_.reserve == 0 && fees_.increment == 0 &&
|
||||
fees_.hookGasPrice == 0,
|
||||
"ripple::Ledger::defaultFees : zero fees");
|
||||
if (fees_.base == 0)
|
||||
fees_.base = config.FEES.reference_fee;
|
||||
@@ -728,6 +745,8 @@ Ledger::defaultFees(Config const& config)
|
||||
fees_.reserve = config.FEES.account_reserve;
|
||||
if (fees_.increment == 0)
|
||||
fees_.increment = config.FEES.owner_reserve;
|
||||
if (fees_.hookGasPrice == 0)
|
||||
fees_.hookGasPrice = config.FEES.hook_gas_price;
|
||||
}
|
||||
|
||||
std::shared_ptr<SLE>
|
||||
|
||||
@@ -29,10 +29,10 @@ namespace ripple {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename value_type>
|
||||
class VotableValue
|
||||
{
|
||||
private:
|
||||
using value_type = XRPAmount;
|
||||
value_type const current_; // The current setting
|
||||
value_type const target_; // The setting we want
|
||||
std::map<value_type, int> voteMap_;
|
||||
@@ -67,8 +67,9 @@ public:
|
||||
getVotes() const;
|
||||
};
|
||||
|
||||
auto
|
||||
VotableValue::getVotes() const -> std::pair<value_type, bool>
|
||||
template <typename value_type>
|
||||
std::pair<value_type, bool>
|
||||
VotableValue<value_type>::getVotes() const
|
||||
{
|
||||
value_type ourVote = current_;
|
||||
int weight = 0;
|
||||
@@ -191,6 +192,18 @@ FeeVoteImpl::doValidation(
|
||||
"reserve increment",
|
||||
sfReserveIncrement);
|
||||
}
|
||||
|
||||
// GasPrice voting (always uses UINT64, independent of featureXRPFees)
|
||||
if (rules.enabled(featureHookGas))
|
||||
{
|
||||
if (lastFees.hookGasPrice != target_.hook_gas_price)
|
||||
{
|
||||
JLOG(journal_.info())
|
||||
<< "Voting for hook gas price of " << target_.hook_gas_price;
|
||||
if (auto const f = target_.hook_gas_price)
|
||||
v[sfHookGasPrice] = f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -213,11 +226,14 @@ FeeVoteImpl::doVoting(
|
||||
detail::VotableValue incReserveVote(
|
||||
lastClosedLedger->fees().increment, target_.owner_reserve);
|
||||
|
||||
detail::VotableValue hookGasPriceVote(
|
||||
lastClosedLedger->fees().hookGasPrice, target_.hook_gas_price);
|
||||
|
||||
auto const& rules = lastClosedLedger->rules();
|
||||
if (rules.enabled(featureXRPFees))
|
||||
{
|
||||
auto doVote = [](std::shared_ptr<STValidation> const& val,
|
||||
detail::VotableValue& value,
|
||||
detail::VotableValue<XRPAmount>& value,
|
||||
SF_AMOUNT const& xrpField) {
|
||||
if (auto const field = ~val->at(~xrpField);
|
||||
field && field->native())
|
||||
@@ -246,7 +262,7 @@ FeeVoteImpl::doVoting(
|
||||
else
|
||||
{
|
||||
auto doVote = [](std::shared_ptr<STValidation> const& val,
|
||||
detail::VotableValue& value,
|
||||
detail::VotableValue<XRPAmount>& value,
|
||||
auto const& valueField) {
|
||||
if (auto const field = val->at(~valueField))
|
||||
{
|
||||
@@ -278,6 +294,33 @@ FeeVoteImpl::doVoting(
|
||||
}
|
||||
}
|
||||
|
||||
// GasPrice voting (UINT64 field, independent of featureXRPFees)
|
||||
if (rules.enabled(featureHookGas))
|
||||
{
|
||||
auto doVote = [](std::shared_ptr<STValidation> const& val,
|
||||
detail::VotableValue<std::uint64_t>& value,
|
||||
SF_UINT64 const& xrpField) {
|
||||
if (auto const field = val->at(~xrpField))
|
||||
{
|
||||
auto const vote = *field;
|
||||
if (vote <= std::numeric_limits<std::uint64_t>::max())
|
||||
value.addVote(vote);
|
||||
else
|
||||
value.noVote();
|
||||
}
|
||||
else
|
||||
{
|
||||
value.noVote();
|
||||
}
|
||||
};
|
||||
for (auto const& val : set)
|
||||
{
|
||||
if (!val->isTrusted())
|
||||
continue;
|
||||
doVote(val, hookGasPriceVote, sfHookGasPrice);
|
||||
}
|
||||
}
|
||||
|
||||
// choose our positions
|
||||
// TODO: Use structured binding once LLVM 16 is the minimum supported
|
||||
// version. See also: https://github.com/llvm/llvm-project/issues/48582
|
||||
@@ -285,11 +328,13 @@ FeeVoteImpl::doVoting(
|
||||
auto const baseFee = baseFeeVote.getVotes();
|
||||
auto const baseReserve = baseReserveVote.getVotes();
|
||||
auto const incReserve = incReserveVote.getVotes();
|
||||
auto const hookGasPrice = hookGasPriceVote.getVotes();
|
||||
|
||||
auto const seq = lastClosedLedger->info().seq + 1;
|
||||
|
||||
// add transactions to our position
|
||||
if (baseFee.second || baseReserve.second || incReserve.second)
|
||||
if (baseFee.second || baseReserve.second || incReserve.second ||
|
||||
(rules.enabled(featureHookGas) && hookGasPrice.second))
|
||||
{
|
||||
JLOG(journal_.warn())
|
||||
<< "We are voting for a fee change: " << baseFee.first << "/"
|
||||
@@ -317,6 +362,10 @@ FeeVoteImpl::doVoting(
|
||||
incReserveVote.current());
|
||||
obj[sfReferenceFeeUnits] = Config::FEE_UNITS_DEPRECATED;
|
||||
}
|
||||
if (rules.enabled(featureHookGas))
|
||||
{
|
||||
obj[sfHookGasPrice] = hookGasPrice.first;
|
||||
}
|
||||
});
|
||||
|
||||
uint256 txID = feeTx.getTransactionID();
|
||||
|
||||
@@ -152,6 +152,18 @@ Change::preclaim(PreclaimContext const& ctx)
|
||||
ctx.tx.isFieldPresent(sfReserveIncrementDrops))
|
||||
return temDISABLED;
|
||||
}
|
||||
// sfHookGasPrice: required when featureHookGas is enabled,
|
||||
// forbidden when disabled
|
||||
if (ctx.view.rules().enabled(featureHookGas))
|
||||
{
|
||||
if (!ctx.tx.isFieldPresent(sfHookGasPrice))
|
||||
return temMALFORMED;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ctx.tx.isFieldPresent(sfHookGasPrice))
|
||||
return temDISABLED;
|
||||
}
|
||||
return tesSUCCESS;
|
||||
case ttAMENDMENT:
|
||||
case ttUNL_MODIFY:
|
||||
@@ -1022,6 +1034,8 @@ Change::applyFee()
|
||||
set(feeObject, ctx_.tx, sfBaseFeeDrops);
|
||||
set(feeObject, ctx_.tx, sfReserveBaseDrops);
|
||||
set(feeObject, ctx_.tx, sfReserveIncrementDrops);
|
||||
if (ctx_.tx.isFieldPresent(sfHookGasPrice))
|
||||
set(feeObject, ctx_.tx, sfHookGasPrice);
|
||||
// Ensure the old fields are removed
|
||||
feeObject->makeFieldAbsent(sfBaseFee);
|
||||
feeObject->makeFieldAbsent(sfReferenceFeeUnits);
|
||||
|
||||
@@ -239,10 +239,16 @@ Transactor::Transactor(ApplyContext& ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static constexpr uint64_t GAS_PRICE_MICRO_DROPS = 1'000'000;
|
||||
|
||||
XRPAmount
|
||||
calculateHookGas(uint32_t gas)
|
||||
Transactor::calculateHookGas(uint32_t gasCount, Fees const& fees)
|
||||
{
|
||||
return XRPAmount{gas};
|
||||
uint64_t const gasPrice =
|
||||
fees.hookGasPrice > 0 ? fees.hookGasPrice : GAS_PRICE_MICRO_DROPS;
|
||||
// TODO: overflow check
|
||||
return XRPAmount{static_cast<XRPAmount::value_type>(
|
||||
(static_cast<uint64_t>(gasCount) * gasPrice) / GAS_PRICE_MICRO_DROPS)};
|
||||
}
|
||||
|
||||
// RH NOTE: this only computes one chain at a time, so if there is a receiving
|
||||
@@ -324,7 +330,8 @@ Transactor::calculateHookChainFee(
|
||||
auto const weakFee = hookObj.isFieldPresent(sfHookWeakGas)
|
||||
? hookObj.getFieldU32(sfHookWeakGas)
|
||||
: hookDef->getFieldU32(sfHookWeakGas);
|
||||
XRPAmount const toAdd = calculateHookGas(weakFee);
|
||||
XRPAmount const toAdd =
|
||||
calculateHookGas(weakFee, view.fees());
|
||||
if (fee + toAdd < fee)
|
||||
fee = XRPAmount{INITIAL_XRP.drops()};
|
||||
else
|
||||
@@ -440,7 +447,7 @@ Transactor::calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||
if (callbackGas > 0)
|
||||
{
|
||||
XRPAmount const toAdd =
|
||||
calculateHookGas(callbackGas);
|
||||
calculateHookGas(callbackGas, view.fees());
|
||||
if (hookExecutionFee + toAdd < hookExecutionFee)
|
||||
hookExecutionFee =
|
||||
XRPAmount{INITIAL_XRP.drops()};
|
||||
@@ -474,7 +481,8 @@ Transactor::calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||
|
||||
if (view.rules().enabled(featureHookGas) &&
|
||||
tx.isFieldPresent(sfHookGas))
|
||||
hookExecutionFee += calculateHookGas(tx.getFieldU32(sfHookGas));
|
||||
hookExecutionFee +=
|
||||
calculateHookGas(tx.getFieldU32(sfHookGas), view.fees());
|
||||
}
|
||||
|
||||
XRPAmount accumulator = baseFee;
|
||||
|
||||
@@ -174,6 +174,9 @@ public:
|
||||
|
||||
// Hooks
|
||||
|
||||
static XRPAmount
|
||||
calculateHookGas(uint32_t gasCount, Fees const& fees);
|
||||
|
||||
static XRPAmount
|
||||
calculateHookChainFee(
|
||||
ReadView const& view,
|
||||
|
||||
@@ -82,6 +82,9 @@ struct FeeSetup
|
||||
/** The per-owned item reserve requirement in drops. */
|
||||
XRPAmount owner_reserve{2 * DROPS_PER_XRP};
|
||||
|
||||
/** The gas price in micro-drops per gas unit. */
|
||||
std::uint64_t hook_gas_price{1'000'000};
|
||||
|
||||
/* (Remember to update the example cfg files when changing any of these
|
||||
* values.) */
|
||||
};
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <boost/regex.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
@@ -1143,6 +1144,11 @@ setup_FeeVote(Section const& section)
|
||||
if (set(temp, "owner_reserve", section))
|
||||
setup.owner_reserve = temp;
|
||||
}
|
||||
{
|
||||
std::int64_t temp;
|
||||
if (set(temp, "hook_gas_price", section))
|
||||
setup.hook_gas_price = temp;
|
||||
}
|
||||
return setup;
|
||||
}
|
||||
} // namespace ripple
|
||||
|
||||
Reference in New Issue
Block a user