Add gas price

This commit is contained in:
tequ
2026-03-30 11:26:12 +09:00
parent 5885be9f8a
commit 8aef5c10f7
15 changed files with 404 additions and 19 deletions

View File

@@ -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)

View File

@@ -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;

View File

@@ -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},

View File

@@ -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)

View File

@@ -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

View File

@@ -66,6 +66,8 @@ STValidation::validationFormat()
{sfBaseFeeDrops, soeOPTIONAL},
{sfReserveBaseDrops, soeOPTIONAL},
{sfReserveIncrementDrops, soeOPTIONAL},
// featureHookGas
{sfHookGasPrice, soeOPTIONAL},
};
// clang-format on

View File

@@ -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);
}
}

View 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

View File

@@ -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>

View File

@@ -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();

View File

@@ -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);

View File

@@ -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;

View File

@@ -174,6 +174,9 @@ public:
// Hooks
static XRPAmount
calculateHookGas(uint32_t gasCount, Fees const& fees);
static XRPAmount
calculateHookChainFee(
ReadView const& view,

View File

@@ -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.) */
};

View File

@@ -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