mirror of
https://github.com/Xahau/xahaud.git
synced 2026-04-29 15:37:46 +00:00
fix(export): compute emit fee before STTx construction
Mutating the fee via const_cast after STTx construction left a stale cached getTransactionID(). When the emitted ttEXPORT was serialised into the emitted directory and later deserialised, the round-tripped txid differed from the original, causing tefNONDIR_EMIT in Transactor::preclaim (the emitted dir entry was keyed with the stale hash). Build a throwaway STTx with fee=0 to calculate the fee size, then construct the real STTx with the correct fee from the start.
This commit is contained in:
@@ -1304,14 +1304,61 @@ HookAPI::xport(Slice const& txBlob) const
|
||||
Serializer innerSer;
|
||||
innerTx->add(innerSer);
|
||||
|
||||
// Build the ttEXPORT wrapper with EmitDetails.
|
||||
// Pre-compute the emit fee so we can set it during STTx
|
||||
// construction. Mutating the fee after construction via
|
||||
// const_cast leaves a stale cached getTransactionID(), which
|
||||
// breaks the tefNONDIR_EMIT check in Transactor::preclaim
|
||||
// after the emitted tx is serialised and deserialised through
|
||||
// the emitted directory round-trip.
|
||||
//
|
||||
// We build a throwaway STTx with fee=0 just for the size
|
||||
// calculation, then construct the real one with the correct fee.
|
||||
uint64_t emitFee = 0;
|
||||
{
|
||||
STTx tmp(ttEXPORT, [&](auto& obj) {
|
||||
obj[sfAccount] = hookCtx.result.account;
|
||||
obj[sfSequence] = 0u;
|
||||
obj.setFieldVL(sfSigningPubKey, Blob{});
|
||||
obj[sfFirstLedgerSequence] = ledgerSeq + 1;
|
||||
obj[sfLastLedgerSequence] = ledgerSeq + 5;
|
||||
obj[sfFee] = STAmount{0};
|
||||
SerialIter sit(innerSer.slice());
|
||||
obj.set(std::make_unique<STObject>(sit, sfExportedTxn));
|
||||
STObject ed(sfEmitDetails);
|
||||
ed.setFieldU32(
|
||||
sfEmitGeneration, static_cast<uint32_t>(etxn_generation()));
|
||||
{
|
||||
auto const b = etxn_burden();
|
||||
ed.setFieldU64(sfEmitBurden, b ? uint64_t(*b) : 1ULL);
|
||||
}
|
||||
ed.setFieldH256(
|
||||
sfEmitParentTxnID, applyCtx.tx.getTransactionID());
|
||||
ed.setFieldH256(sfEmitNonce, *nonce);
|
||||
ed.setFieldH256(sfEmitHookHash, hookCtx.result.hookHash);
|
||||
if (hookCtx.result.hasCallback)
|
||||
ed.setAccountID(sfEmitCallback, hookCtx.result.account);
|
||||
obj.set(std::move(ed));
|
||||
});
|
||||
Serializer feeSer;
|
||||
tmp.add(feeSer);
|
||||
auto feeResult = etxn_fee_base(feeSer.slice());
|
||||
if (!feeResult)
|
||||
{
|
||||
JLOG(j.trace()) << "HookExport[" << HC_ACC()
|
||||
<< "]: Fee calculation failed for ttEXPORT wrapper";
|
||||
return Unexpected(EXPORT_FAILURE);
|
||||
}
|
||||
emitFee = static_cast<uint64_t>(*feeResult);
|
||||
}
|
||||
|
||||
// Build the ttEXPORT wrapper with the correct fee.
|
||||
STTx exportStx(ttEXPORT, [&](auto& obj) {
|
||||
obj[sfAccount] = hookCtx.result.account;
|
||||
obj[sfSequence] = 0u;
|
||||
obj.setFieldVL(sfSigningPubKey, Blob{});
|
||||
obj[sfFirstLedgerSequence] = ledgerSeq + 1;
|
||||
obj[sfLastLedgerSequence] = ledgerSeq + 5;
|
||||
obj[sfFee] = STAmount{0}; // emitted txns have special fee handling
|
||||
obj[sfFee] = STAmount{emitFee};
|
||||
|
||||
// sfExportedTxn inner object
|
||||
SerialIter sit(innerSer.slice());
|
||||
@@ -1336,21 +1383,6 @@ HookAPI::xport(Slice const& txBlob) const
|
||||
obj.set(std::move(emitDetails));
|
||||
});
|
||||
|
||||
// Calculate proper fee for the emitted transaction.
|
||||
{
|
||||
Serializer feeSer;
|
||||
exportStx.add(feeSer);
|
||||
auto feeResult = etxn_fee_base(feeSer.slice());
|
||||
if (!feeResult)
|
||||
{
|
||||
JLOG(j.trace()) << "HookExport[" << HC_ACC()
|
||||
<< "]: Fee calculation failed for ttEXPORT wrapper";
|
||||
return Unexpected(EXPORT_FAILURE);
|
||||
}
|
||||
const_cast<STTx&>(exportStx).setFieldAmount(
|
||||
sfFee, STAmount{static_cast<uint64_t>(*feeResult)});
|
||||
}
|
||||
|
||||
// Preflight the wrapper.
|
||||
auto preflightResult = ripple::preflight(
|
||||
app, view.rules(), exportStx, ripple::ApplyFlags::tapPREFLIGHT_EMIT, j);
|
||||
|
||||
Reference in New Issue
Block a user