Repository navigation
Conversation
NumaryBot
previously approved these changes
Oct 21, 2026
NumaryBot
left a comment
Contributor
There was a problem hiding this comment.
The required automated review completed with no remaining findings.
The compiler now ends every send statement with an uncapped send, and the verifier rejects a path from a pull to set_current_asset or the end of the run without one, in one forward pass.
ascandone
force-pushed
the
feat/vm-verify-queue-drained
branch
from
October 22, 2026 08:53
cc0bdd3 to
127609f
Compare
NumaryBot
approved these changes
Oct 22, 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 22, 2026 08:55
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.
Stacked on #225.
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 at run time:set_current_assetruns are posted under the new asset.Whether a capped send empties the queue depends on the balances, so on today's bytecode this is not decidable. For example,
pull(cap: 100)thensend(cap: 50)is fine with a balance of 40 and leaves 30 queued with a balance of 80. The compiler empties the queue with a chain of capped sends whose caps add up to the amount pulled, which the verifier cannot prove.Fix
Make draining the queue something the bytecode shows, then check it.
remainingof aninorder, eachoneofbranch, and a lone account orkept. Theinorderbranch that skips a zeroremainingdrains with an uncappedkept(send_to_account()), which only clears zero-amount entries and emits no posting. Postings are unchanged: zero-amount postings are already dropped, so an uncapped tail sends what the capped one did.checkQueueDrainedkeeps one bool per instruction, "may hold pulled funds". A pull sets it, an uncapped send clears it, and capped sends and rewinds leave it alone. The program is rejected if the bool can be true atset_current_assetor at the end of the run. Jumps only go forward, so this is one pass over the instructions, and it never looks at amounts.The rule is conservative. It rejects some programs that would be fine: capped chains that add up,
send(cap: $pulled), and regions that are always rewound. The rejected and accepted shapes are listed inTestIRVerifierRequiresAnUncappedSendAfterEveryPulland inbytecode-verifier.md.Execdoes not check the queue. The twoTODO crashescomments invm.goare removed, sinceVerifyalready rejects a truncated two-word instruction.Open: bytecode version
Bytecode from the previous compiler ends allotments with capped sends, so it fails the new rule. Bumping
CurrentBytecodeVersionto 0.2 would make old blobs fail at decode instead, since a 0.x reader accepts only its exact version. This PR does not bump it.Tests
TestDestInorder) changes.go test ./..., the difftest module (which verifies every generated program) andTestCompiledCorpusPassesVerifypass.Performance
BenchmarkCompiledVM*, minimum of 5 runs: allotment benchmarks use 3 fewer allocs (42 → 39, 58 → 55) and are about 1.5% faster. The others are within noise.