etxn_fee_base: compute the fee against the applied view (fix20261001) (#842)

This commit is contained in:
tequ
2026-09-29 04:02:37 +09:00
committed by GitHub
parent f155556017
commit 4d08a9e603
2 changed files with 66 additions and 7 deletions

View File

@@ -732,6 +732,56 @@ public:
else
BEAST_EXPECT(result.value() == baseFee + memoSize);
}
{
// fix20261001: the minimum fee is computed against the view
// being applied, not the node-local open ledger. Simulate that
// by inserting a hook definition (with a callback fee) into
// applyCtx.view() only -- it is never inserted into the open
// ledger the node otherwise reads.
uint256 const hookHash{1};
STTx const emittedTx = STTx(ttINVOKE, [&](STObject& obj) {
obj[sfAccount] = alice.id();
obj[sfSequence] = 0;
obj[sfSigningPubKey] = Slice{};
obj[sfFirstLedgerSequence] = ov.seq() + 1;
obj[sfLastLedgerSequence] = ov.seq() + 5;
obj[sfFee] = env.closed()->fees().base;
auto& emitDetails = obj.peekFieldObject(sfEmitDetails);
emitDetails[sfEmitGeneration] = 1;
emitDetails[sfEmitBurden] = 1;
emitDetails[sfEmitParentTxnID] = invokeTx.getTransactionID();
emitDetails[sfEmitNonce] = uint256();
emitDetails[sfEmitHookHash] = hookHash;
});
Serializer const emittedTxSerializer = emittedTx.getSerializer();
auto const baseline =
api.etxn_fee_base(emittedTxSerializer.slice());
BEAST_EXPECT(baseline.has_value());
auto hookDef =
std::make_shared<SLE>(keylet::hookDefinition(hookHash));
hookDef->setFieldH256(sfHookHash, hookHash);
hookDef->setFieldH256(sfHookNamespace, uint256());
hookDef->setFieldArray(sfHookParameters, STArray{});
hookDef->setFieldU16(sfHookApiVersion, 0);
hookDef->setFieldVL(sfCreateCode, std::vector<uint8_t>{});
hookDef->setFieldH256(sfHookSetTxnID, invokeTx.getTransactionID());
hookDef->setFieldU64(sfReferenceCount, 1);
hookDef->setFieldAmount(sfFee, XRPAmount{0});
hookDef->setFieldAmount(sfHookCallbackFee, XRPAmount{12345});
applyCtx.view().insert(hookDef);
auto const result = api.etxn_fee_base(emittedTxSerializer.slice());
BEAST_EXPECT(result.has_value());
if (env.closed()->rules().enabled(fix20261001))
BEAST_EXPECT(result.value() == baseline.value() + 12345);
else
BEAST_EXPECT(result.value() == baseline.value());
}
}
void
@@ -4718,6 +4768,7 @@ public:
test_otxn_generation(features);
test_etxn_details(features);
test_etxn_fee_base(features - fixHookAPI20251128);
test_etxn_fee_base(features - fix20261001);
test_etxn_fee_base(features);
test_etxn_nonce(features);
test_etxn_reserve(features);

View File

@@ -868,14 +868,22 @@ HookAPI::etxn_fee_base(ripple::Slice const& txBlob) const
std::unique_ptr<STTx const> stpTrans =
std::make_unique<STTx const>(std::ref(sitTrans));
if (!hookCtx.applyCtx.view().rules().enabled(fixHookAPI20251128))
return Transactor::calculateBaseFee(
*(applyCtx.app.openLedger().current()), *stpTrans)
.drops();
// fix20261001: the minimum fee decides whether emit() accepts the
// txn, which is consensus-visible. Compute it against the view being
// applied, not the node-local open ledger (whose hook definitions can
// differ between nodes).
std::shared_ptr<ReadView const> hold;
ReadView const* feeView = &applyCtx.view();
if (!applyCtx.view().rules().enabled(fix20261001))
{
hold = applyCtx.app.openLedger().current();
feeView = hold.get();
}
return invoke_calculateBaseFee(
*(applyCtx.app.openLedger().current()), *stpTrans)
.drops();
if (!hookCtx.applyCtx.view().rules().enabled(fixHookAPI20251128))
return Transactor::calculateBaseFee(*feeView, *stpTrans).drops();
return invoke_calculateBaseFee(*feeView, *stpTrans).drops();
}
catch (std::exception const& e)
{