Repository navigation
fix(vm): fail closed when pulled funds are left in the queue - #217
Merged
Merged
Conversation
NumaryBot
previously approved these changes
Oct 12, 2026
NumaryBot
left a comment
Contributor
There was a problem hiding this comment.
The required automated review completed with no remaining findings.
ascandone
marked this pull request as draft
October 14, 2026 06:56
NumaryBot
reviewed
Oct 21, 2026
Exec returns an InternalError when set_current_asset runs, or the run ends, while the source queue still holds a positive amount. Whether a capped send drains the queue depends on the balances, so this is not checked by Verify.
ascandone
force-pushed
the
fix/vm-malformed-pull-send
branch
from
October 28, 2026 10:45
665f356 to
c5639f4
Compare
ascandone
marked this pull request as ready for review
October 28, 2026 10:57
NumaryBot
previously approved these changes
Oct 29, 2026
NumaryBot
left a comment
Contributor
There was a problem hiding this comment.
The required automated review completed with no remaining findings.
NumaryBot
approved these changes
Oct 29, 2026
NumaryBot
left a comment
Contributor
There was a problem hiding this comment.
The required automated review completed with no remaining findings.
gfyrag
approved these changes
Oct 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A pull debits its source at once; the posting only exists once a send drains the queue. Two shapes pass
vm.Verifyand go wrong with no error:set_current_assetruns are posted under the new asset.Compiled numscript never emits either: every send statement either pulls exactly what it sends (
check_enough_funds) or sends everything it pulled. They matter for bytecode that did not come out of this process's compiler, which is what the ledger replays.Fix
Execreturns anInternalErrorfor both, via a newfunds.RunState.HasQueuedFunds():errSetAssetWithQueuedFundsatset_current_asseterrEndWithQueuedFundsat the end of the runHasQueuedFunds()is a length check. For that to hold,Pullno longer queues a zero amount, matchingPullUncapped. Before, a zero-cap or empty-source pull queued a zero entry, and a send that stopped once its cap was met left it in the queue (e.g.send [USD 0]). A zero entry never produced a posting, so outputs are unchanged.This is a run-time check because it is not statically decidable on the current bytecode: whether a capped send empties the queue depends on the balances.
bytecode-verifier.mddocuments it under "What it does not check". #226 is the static alternative, at the cost of a compiler change and a bytecode version bump.The two
TODO crashescomments on the ext-word reads invm.goare removed:Verifyrejects a truncated two-word instruction, andExecis total only for verified programs.Not changed
Verifysince fix(vm): reject a pull whose destination aliases its cap or overdraft #225, not checked again inExec.Execon unverified bytecode can still panic (truncated instruction, out-of-range register or pool index); verify-first remains the contract.Tests
vm_test.go: each check, plus a zero-amount pull that must not trip it.ir_test.go: both shapes as IR, so they sit in the "sequences the compiler never emits" corpus.go test ./...(including the frozen bytecode corpus) and the difftest module pass.Performance
The run-time checks themselves:
BenchmarkCompiledVM*, 8 runs each againstmain, benchstat: no regression, identical B/op and allocs/op.Not queuing zero amounts, 6 interleaved runs against the previous commit:
{@a @b @c},@aand@bempty (ad hoc, not committed)CompiledVMAllotmentCompiledVMCapped