Feat/pow inner computer refactor - #486
Open
ltardivo wants to merge 6 commits into
Open
Conversation
Guard first, prev, next, last, and load with confirmed-location checks; require confirmed successors/spends for next/last. Expand InnerComputer tests for unconfirmed starts, mempool next, spent last, and sync. fix (monorepo): Adapt apps and docs to confirmed InnerComputer queries Mine after deletes before finalWithdraw; confirm modules and mints in tests. Chess helper waits for tip confirmation before withdraw/cancel refund. Document observation stability and contract-vs-client computer APIs in docs and docs-2.
Add formatInvalidStateError so the standard forbidden phrase is appended at most once from _safeCall and Db.eval. Cover uncaught, catch-and-continue, and short policy messages in tests. (docs): document single-suffix InnerComputer invalidation errors Update Contract querying and docs-2 intro/how-it-works/sandbox pages so public invalidation messages always end with one forbidden suffix on uncaught and catch-and-continue paths.
SES free-variable `computer` is bound to the definition compartment (create / Modules.load), which is often a different InnerComputer instance than the one Db.eval endows for the current call. A pure instance flag therefore missed catch-and-continue invalidations after V4 instance-local state. Introduce beginEvalInvalidation/endEvalInvalidation stack frames as the authoritative invalidation record: _invalidate marks the current top frame (and mirrors on the instance). Db.eval and Modules.load each push/pop around evaluate/import so concurrent Promise.all evals cannot cross-talk, while module bodies that call computer still participate. Tests cover free-var mismatch, nested frames, concurrent unfunded encode isolation, and module-deployed classes with try/catch still rejected after evaluate returns. docs: document eval-stack invalidation and SES free-var computer Explain that invalidation is tracked per evaluation frame (push/pop in Db.eval and Modules.load), not a process-global flag, so concurrent evaluations stay isolated. Note that free-variable computer may resolve to the create-time or module-load instance while the active frame still records invalidation—catch-and-continue cannot soft-succeed. Update Lib/Contract querying and docs-2 sandbox architecture accordingly.
Replace the plain invalidation stack with withEvalInvalidation dual backends:
Node uses AsyncLocalStorage via process.getBuiltinModule('async_hooks') (no
static node:async_hooks import), and the browser uses an await-scoped stack
with serialized root frames so concurrent Promise.all evals cannot cross-talk
under SES lockdown.
Harden the host path and error shape:
- Db.eval prefers frame.invalid / frame.msg on both compartment throw and
catch-and-continue return, not computer.isInvalid alone.
- _invalidate and _safeCall always apply formatInvalidStateError (single
forbidden suffix; policy reasons never stand alone).
- _ensureConfirmedTx treats any falsy block hash as unconfirmed.
- createInnerComputerEndowment remains a hardened public-method facade.
Stabilize InnerComputer tests against indexer lag and mixed unconfirmed
dispatch (confirm the query object first; poll outer getTXOs before asserting
in-contract results; expectSingleForbiddenSuffix everywhere).
Includes modules.ts wiring for load-time frames.
(docs): align InnerComputer sandbox docs with eval-frame implementation
Document dual-runtime evaluation frames (Node ALS without a static node:async_hooks import; browser stack + serialized roots), host reject on
frame.invalid for throw and catch-and-continue, and the canonical single forbidden-suffix error shape for policy and missing observations.
- Add Lib/Contract/sandbox-and-inner-computer.md and link it from Contract querying, Lib index, how-it-works, and language.
- Refresh Contract querying invalidation / error / getTXOs wording.
- Sync docs-2 architecture sandbox page, intro, and how-it-works notes with the same model (drop obsolete plain eval-stack-only wording).
Build SES compartment globals via buildContractCompartmentGlobals so host console is available only in client mode dev/debug. In prod, console is not in scope (contracts must not rely on it). Simplify without changing security semantics: - Shared globals helper for Db.eval and Modules.load - Single invalidation exit in Db.eval (capture frame.msg before resetInvalid) - Same host pattern for Modules.load import failures - Drop redundant formatInvalidStateError in _safeCall - Slim stack-frame pop in withEvalInvalidation (docs): no console in prod contracts; refresh sandbox notes Document that compartment host console is endowed only in dev/debug client mode and must not be used in production smart contracts (ReferenceError in prod). Update sandbox-and-inner-computer, Contract querying, how-it-works, language, and docs-2 architecture accordingly.
Document query-only endowment, host frame authority, and free-var routing to the active eval client. Drop obsolete isInvalid / resetInvalid contract APIs.
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.
This PR tightens the determinism and fail-closed guarantees of the
InnerComputerthat every smart-contract method sees. Successful queries must now be stable under chain extension: unconfirmed locations, mempool-only successors, unspent tips treated aslast, and unguarded TXO queries all invalidate the evaluation—even when the contract catches the thrown error. Invalidation is tracked per evaluation frame, the compartment endowment is a hardened public-method facade, and every public error ends with exactly one copy of the standard forbidden suffix.Lib
sync,decode,load,first,prev,next,last, block-time/height/hash helpers, andgetTXOs(plus aliases).nextrequires a confirmed successor;lastrequires a confirmed spending input of the tip (unspent or mempool-only spend invalidates).Inner Computer Endowmentexposes only bound public methods + getters, hardened after SES lockdown; method replacement, prototype tampering, and getter shadowing are blocked.next, confirmed-spendlast, and indexer-lag stabilizers.Contracts & tokens
finalWithdrawandcomputeFinalWithdrawdocumentation and tests now require the tip to be spent in a confirmed transaction beforecomputer.lastsucceeds. Tests mine afterdelete.ChessContractHelpergainswaitForConfirmed;withdrawTokensandcancelGameAndWithdrawwait for the chess tip before the TBC777 audit. UI copy and README updated.calculateTimes/ timeout helpers document the confirmed-prev-chain requirement; new regression test.claimfails closed on unconfirmed mint creation (uses block-height / decode / OTXO paths).Apps
Docs
Lib/Contract/sandbox-and-inner-computer.mdand docs-2 architecture counterpart.last, stabilize TXO queries).Breaking / migration notes
Call sites that previously observed unconfirmed tips or mempool state inside contracts will now invalidate:
deploy/new/ method calls /delete, wait for confirmation before any on-chain code that usesload,sync,first/prev/next/last, or block times on those locations.deletethe tip → mine → thenfinalWithdraw(tipRev).message.endsWith('Accessing non-existent on-chain state inside a smart contract is forbidden.')(or the shared helper); short policy reasons never stand alone.