Skip to content

finish fn result refactor - #7947

Open
pwang200 wants to merge 1 commit into
ripple/smart-escrowfrom
ripple/smart-escrow-finish-return
Open

finish fn result refactor#7947
pwang200 wants to merge 1 commit into
ripple/smart-escrowfrom
ripple/smart-escrow-finish-return

Conversation

@pwang200

@pwang200 pwang200 commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

High Level Overview of Change

Makes EscrowFinish's handling of the finish function's result explicit. Three rules:

  • Gas is reported in the transaction metadata as sfGasUsed whenever the engine reports a cost: the function ran to completion, ran out of gas, or trapped. It is not reported when the result is tecINTERNAL.
  • Data written through set_data is stored only when the function ran to completion, and it survives only on a reject, where the escrow is not deleted.
  • Return value of 1 or greater releases the escrow, with tesSUCCESS. 0 or negative is a reject: the escrow stays, the result is tecBYTECODE_REJECTED, and the value is reported as sfVMReturnCode.

@xrplf-ai-reviewer xrplf-ai-reviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a clean, well-reasoned refactor of EscrowFinish's result handling that matches the three stated rules (gas reporting excludes tecINTERNAL, data persistence only on completed runs, return-code semantics for accept/reject). I traced the control flow carefully: gas is set via ctx_.setGasUsed for both the success and error paths (using the unified cost optional) before any early return, data writes now only occur after confirming re.has_value() (fixing the prior bug where partial data could be written even on trap/out-of-gas), and the tecINTERNAL override in ApplyContext::apply correctly nulls out any gas value set earlier in doApply when a tecINTERNAL is ultimately returned. The bound check *cost < 0 || *cost > allowance is a reasonable invariant check (defensive, LCOV_EXCL_LINE) consistent with the engine's contract described in the comments. Cast to std::uint32_t after the bounds check is safe. I did not find any correctness, security, or resource issues in the changed lines; the added test assertions in EscrowSmart_test.cpp look consistent with the new behavior.

@codecov

codecov Bot commented Aug 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@pwang200
pwang200 requested a review from mvadari August 4, 2026 14:16
// 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() ? std::optional<std::int64_t>{re->cost} : re.error().cost;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit

Suggested change
re.has_value() ? std::optional<std::int64_t>{re->cost} : re.error().cost;
re.has_value() ? re->cost : re.error().cost;

// should already be checked in the updateData host function
// 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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Can we change the cost to be unsigned to avoid this need for checking for a negative gas cost?

// recorded before we hit it is not trustworthy, so it is left unreported.
// NOLINTNEXTLINE(bugprone-unchecked-optional-access) view_ emplaced in constructor
view_->setGasUsed(gasUsed_);
view_->setGasUsed(ter == tecINTERNAL ? std::nullopt : gasUsed_);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think there might be some confusing behavior between this and this line above view_->setVMReturnCode(*vmReturnCode_);?

For instance there is a wasm return code but there is no gas used?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants