finish fn result refactor (#7947)

This commit is contained in:
pwang200
2026-08-08 23:13:33 -04:00
committed by GitHub
parent 75fac65e27
commit ef438aec26
3 changed files with 77 additions and 28 deletions

View File

@@ -58,14 +58,19 @@ ApplyContext::discard()
std::optional<TxMeta>
ApplyContext::apply(TER ter)
{
if (vmReturnCode_.has_value())
// tecINTERNAL reports an xrpld bug, not a result: nothing the VM recorded
// before we hit it belongs in the metadata.
if (ter != tecINTERNAL)
{
if (vmReturnCode_.has_value())
{
// NOLINTNEXTLINE(bugprone-unchecked-optional-access) view_ emplaced in constructor
view_->setVMReturnCode(*vmReturnCode_);
}
// NOLINTNEXTLINE(bugprone-unchecked-optional-access) view_ emplaced in constructor
view_->setVMReturnCode(*vmReturnCode_);
view_->setGasUsed(gasUsed_);
}
// NOLINTNEXTLINE(bugprone-unchecked-optional-access) view_ emplaced in constructor
view_->setGasUsed(gasUsed_);
// NOLINTNEXTLINE(bugprone-unchecked-optional-access) view_ emplaced in constructor
return view_->apply(base_, tx, ter, parentBatchId_, (flags_ & TapDryRun) != 0u, journal);
}

View File

@@ -35,7 +35,6 @@
#include <xrpl/tx/wasm/WasmVM.h>
#include <cstdint>
#include <limits>
#include <memory>
#include <optional>
#include <string>
@@ -417,9 +416,35 @@ EscrowFinish::doApply()
return tecINTERNAL;
}
std::uint32_t const allowance = ctx_.tx[sfGas];
auto re = runEscrowWasm(wasm, ledgerDataProvider, allowance, escrowFunctionName);
auto const re = runEscrowWasm(wasm, ledgerDataProvider, allowance, escrowFunctionName);
JLOG(j_.trace()) << "Escrow WASM ran";
// Gas consumed, reported in the tx metadata whenever the engine has a
// trustworthy number: a completed run, out of gas, or a wasm fault.
std::optional<std::int64_t> const cost = re.has_value() ? re->cost : re.error().cost;
if (cost.has_value())
{
// The engine cannot spend more than it was given, and pins the cost
// to the allowance when it runs out.
if (*cost < 0 || *cost > allowance)
return tecINTERNAL; // LCOV_EXCL_LINE
ctx_.setGasUsed(static_cast<std::uint32_t>(*cost));
}
if (!re.has_value())
{
// No return code, and any data it wrote goes away with the view.
JLOG(j_.debug()) << "WASM Failure: " + transHuman(re.error().ter);
return re.error().ter;
}
auto const reValue = re->result;
JLOG(j_.debug()) << "WASM Success: " + std::to_string(reValue) << ", cost: " << re->cost;
ctx_.setVMReturnCode(reValue);
// Only matters on a reject, where the escrow survives:
// Transactor::processPersistentChanges replays this after the reset.
if (auto const& data = ledgerDataProvider.getData(); data.has_value())
{
if (data->size() > kMaxWasmDataLength)
@@ -431,28 +456,9 @@ EscrowFinish::doApply()
ctx_.view().update(slep);
}
if (re.has_value())
{
auto const reValue = re.value().result;
auto const reCost = re.value().cost;
JLOG(j_.debug()) << "WASM Success: " + std::to_string(reValue) << ", cost: " << reCost;
ctx_.setVMReturnCode(reValue);
if (reCost < 0 || reCost > std::numeric_limits<uint32_t>::max())
return tecINTERNAL; // LCOV_EXCL_LINE
ctx_.setGasUsed(static_cast<uint32_t>(reCost));
if (reValue <= 0)
{
return tecBYTECODE_REJECTED;
}
}
else
{
JLOG(j_.debug()) << "WASM Failure: " + transHuman(re.error().ter);
return re.error().ter;
}
// 0 or negative is a contract-defined reject code, reported as sfVMReturnCode.
if (reValue <= 0)
return tecBYTECODE_REJECTED;
}
AccountID const account = (*slep)[sfAccount];

View File

@@ -492,6 +492,18 @@ struct EscrowSmart_test : public beast::unit_test::Suite
Fee(finishFee),
escrow::Gas(2),
Ter(tecOUT_OF_GAS));
// Running out of gas still reports the gas consumed, which is the
// whole allowance. The function did not run to completion, so
// there is no return code to report.
auto const txMeta = env.meta();
if (BEAST_EXPECT(txMeta && txMeta->isFieldPresent(sfGasUsed)))
{
BEAST_EXPECTS(
txMeta->getFieldU32(sfGasUsed) == 2,
std::to_string(txMeta->getFieldU32(sfGasUsed)));
}
BEAST_EXPECT(txMeta && !txMeta->isFieldPresent(sfVMReturnCode));
}
{
@@ -510,6 +522,32 @@ struct EscrowSmart_test : public beast::unit_test::Suite
escrow::Gas(allowance),
Ter(tefNO_BYTECODE));
}
{
// a trap in the wasm code reports the gas it burned, which is only
// part of the allowance
auto const trapSeq = env.seq(alice);
env(escrow::create(alice, carol, XRP(500)),
escrow::Bytecode(kTrapUnreachableHex),
escrow::kCancelTime(env.now() + 100s),
Fee(env.current()->fees().base * 10 + kTrapUnreachableHex.size() / 2 * 5));
env.close();
std::uint32_t const allowance = 1000;
env(escrow::finish(carol, alice, trapSeq),
Fee(env.current()->fees().base +
(allowance * env.current()->fees().gasPrice) / microDropsPerDrop + 1),
escrow::Gas(allowance),
Ter(tecFAILED_PROCESSING));
auto const txMeta = env.meta();
if (BEAST_EXPECT(txMeta && txMeta->isFieldPresent(sfGasUsed)))
{
auto const gasUsed = txMeta->getFieldU32(sfGasUsed);
BEAST_EXPECTS(gasUsed < allowance, std::to_string(gasUsed));
}
BEAST_EXPECT(txMeta && !txMeta->isFieldPresent(sfVMReturnCode));
}
}
void