Skip to content

[0.32][Language/ABI] Stabilize generic compatible-open Script and verifier handles #28

Closed

Description

@a19q3

Completed — 2026-10-12

Implemented on branch 0.32; clean-source executable acceptance is bound to 16b9e58394aa182f071303e6b5ab15dc94d304f4. The acceptance mapping maps each required boundary to executable evidence. Its original pending-backend status is superseded by the passing clean replay recorded here; the final documentation-only refresh is prepared locally but awaits GPG unlock.

  • Nominal ScriptHandle/VerifierHandle, independently recomputed receipts, immutable member policies, exact dependency/Script/network identities and Type-hash history binding are implemented.
  • Lock and Type consumers admit selected Type-policy, single Type/Lock and parameterless SPAWN/WAIT verifier profiles. Unsupported public codecs fail closed; arbitrary child argv ABI and behavioral equivalence are not claimed.
  • O0–O3 Lock/verifier transaction matrices cover identical native/CLI/generated-TypeScript/SDK bytes, actual Script-group execution, failing Lock predicates and spawned children, receipt/machine/role/dependency substitution, and signing boundaries.
  • Full backend log SHA-256: d768615c34b8054b907448347606b9e01fdfbbd7206101a8352ed2296b191269; strict backend report SHA-256: 093138c609ab64847338683c6c34117a33fb6738e01bf6c480322eaee6fa12cf. Both report success.
  • Full ci, clean-source backend (including pinned CKB stateful scenarios and counter_node_acceptance) and final commit gates passed. The CKB receipt binds source commit 16b9e583 with git_dirty=false; its SHA-256 is a144be567c14c35702034370f7ccc1c76ded743441560528a45425829a36908c.
  • External human audit remains waived by the maintainer; no such audit is claimed. The frozen profile and resource limits in the design/acceptance record remain part of the admitted contract.

Maintainer decision — 2026-10-11: external audit requirement cancelled

The maintainer explicitly cancelled the external audit / independent human security review requirement for #28. Assigning an external reviewer is no longer a closure or stable-admission gate for this issue. This supersedes earlier issue text and comments requiring that review; it does not claim an audit occurred.

The independent artifact checker, receipt recomputation, compiler/checker agreement, adversarial CKB-VM tests, Type-hash history binding, generated-builder/ProtocolBundle byte parity, deterministic identities, resource budgets and applicable unified gates remain required. Unfinished technical acceptance still prevents closing the issue. The maintainer subsequently waived #29's external human review requirement as well. Neither waiver authorizes release publication or deployment.

Accepted scope update — 2026-10-04

The maintainer promoted this issue into the 0.32 implementation scope. Earlier post-v1/deferred wording below records its original separation from 0.30; it is no longer the current version assignment. All required contracts and acceptance evidence remain applicable. The original external review requirement is superseded by the maintainer decision below; all technical acceptance criteria remain required. Implementation and technical acceptance are complete; see the completion record above.

Summary

Track the generic compatible/open handle model that is deliberately outside CellScript 0.30's exact fixed-width handle closure in #11.

CellScript 0.30 supports exact artifact-bound ExactScriptHandle and exact-version deployment-line handles. This issue must not weaken those identities into a producer-supplied compatibility bit. It owns a future contract for selecting one implementation at construction time from an explicitly compatible, independently verified set.

Required contract

  • Define ScriptHandle<I> / VerifierHandle<I> as nominal values distinct from raw CKB Script, artifact identity, deployment identity, and behavioral equivalence.
  • Recompute the complete public interface receipt from independently checked artifact records, including nested layouts, callable/witness ABI, effects, target/profile, source codec, and compatibility axes.
  • Bind the concrete code bytes/CellDep actually selected by the transaction, including Type-hash replacement history.
  • Specify exact-version, compatible-version, active/yanked, downgrade, and stale-receipt semantics.
  • Define an immutable on-chain commitment model; Registry discovery alone is not authorization.
  • Preserve full Script role, code_hash, hash_type, args policy, deployment line, and network identity.
  • Make ProtocolBundle reject missing, duplicate, conflicting, unverified, or post-build-substituted receipts before signing/materialization.
  • Keep source and CKB runtime values fixed or explicitly bounded; no arbitrary dynamic allocation.

Acceptance evidence

  • A versioned design record and threat model covering data-hash and Type-hash deployments.
  • Independent checker recomputation of every receipt field it owns.
  • Positive exact and compatible selections plus nested-layout, ABI, target, args, CellDep, yanked, downgrade, and substitution negatives.
  • Generated-builder and ProtocolBundle parity on identical transaction bytes.
  • CKB-VM enforcement for every claim represented as on-chain; builder-only claims remain labeled off-chain.
  • Deterministic artifact, receipt, transaction, and resource identities.

Non-goals

Relationship

Split from #11 after its bounded 0.30 exact-handle scope was implemented. This is post-v1/deferred work and is not a 0.30 release blocker unless a later accepted scope explicitly promotes it.

Activity

  1. added a commit that references this issue on Oct 10, 2026
  2. a19q3 commented on Oct 10, 2026

    @a19q3
    CollaboratorAuthor

    Design-record deliverable published at 37f4c602 on 0.32: the versioned design record and threat model CELLSCRIPT_OPEN_HANDLE_DESIGN.md (cellscript-open-handle-design-v1). It consolidates the implemented boundary — authorization-set wire and CCC vectors, finite Type-policy receipts, native v1/v2 policy/source bindings with exact/compatible selection, SemVer precedence, direct-dependency and Type-group transaction snapshots — into the six design decisions, a data-hash versus Type-hash deployment profile split, and a thirteen-entry threat table (T1–T13) mapping each adversary action to its enforcing boundary with an explicit enforced/pending/non-goal state. Residual risks before stable admission are listed: finite-profile exclusions fail closed, root authorization is host-side only, runtime membership execution is unimplemented, builder/ProtocolBundle parity is unintegrated, worst-case runtime budgets are unmeasured, and the independent security review remains unassigned. This satisfies the first acceptance-evidence item of the issue; it is a progress record, not closure.

    Remaining for H1: private template structural/phantom-use projection, universal/absent template coverage, complete nominal qualification for ambiguous unqualified arguments, full directional receipt compatibility, and the complete general codec/builder/executable obligations beyond the policy-unit-scalars-flat-unsigned-cell-v1 profile. Remaining for H2: nominal ScriptHandle<I>/VerifierHandle<I> source values, class/role separation, immutable root binding to committed code/args, and CKB-VM enforcement with measured budgets. Generated-builder/ProtocolBundle byte parity, the final unified gates and signed publication follow those. The independent security review (not covered by the 2026-10-03 waiver) remains a final stable-admission requirement; closing this issue before those contracts and evidence exist would contradict its own acceptance list.

  3. a19q3 commented on Oct 10, 2026

    @a19q3
    CollaboratorAuthor

    Implementation progress at 7c54d759 on 0.32 (maintainer authorized continuing the H1 leftover items, then H2; this issue remains open).

    • Private template phantom-use projection now lands in the checked module projection. A concrete instance retained only as an identity dependency — for example a nominal referenced exclusively through a phantom type argument, named by no public spelling — previously produced no projection contracts at all: a required module and a candidate that silently swapped the phantom nominal (or the non-phantom argument) were directionally indistinguishable. The projection closure now seeds every retained struct/enum instantiation's template and fully qualified type arguments, so nominal:/layout: contracts cover the template, the concrete instance and the phantom-only argument nominal itself. Structural use of private templates through public spellings was already projected and is unchanged.
    • Directional semantics are pinned by tests at O0–O3: an implementation that adds internal identity dependencies remains a valid candidate (additions allowed; the reverse direction still requires them), while swapping the phantom-only nominal, changing the non-phantom argument, or altering the phantom nominal's own layout rejects. Mutation coverage lives in tests/interface_projection.rs::retained_private_instances_project_phantom_identity_dependencies; crates/cellscript-artifact-checker/src/interface/projection.rs carries the closure seeding with the existing 256-nominal and 4,096-visit bounds unchanged.
    • Validation: interface_projection (8), interface_inspection (10), policy_artifact_checker (71), frozen_interface (38), checker lib (40) and open_handle_policy (9) all pass single-threaded; checker clippy --all-targets and fmt clean; commit gate passed. No producer metadata, wire schema or cache identity changed — bundles keep their existing sidecar identities; only newly derived projection contracts differ where retained instances exist.
    • The receipt contract and participants plan record the new boundary.

    Remaining H1 items: universal/absent template coverage in symbolic verification, complete nominal qualification for ambiguous unqualified instantiation arguments, full directional receipt compatibility (effect weakening/binder relaxation), and the general codec/builder/executable obligations beyond policy-unit-scalars-flat-unsigned-cell-v1. Then H2 nominal handles and CKB-VM enforcement, builder/ProtocolBundle parity, final unified gates and the still-unassigned independent security review. Not closing this issue yet.

  4. a19q3 commented on Oct 10, 2026

    @a19q3
    CollaboratorAuthor

    Implementation progress at 8cfd1524 on 0.32 (second H1 leftover item landed; issue remains open).

    • Universal/absent template coverage now sweeps the entire retained generic declaration catalog instead of public declared items alone. Private templates and declarations that no retained instantiation exercises carry the same parameter-minima and symbolic-ability contract as public ones — an absent template cannot derive ability evidence from an instance that does not exist — and retained function templates must keep their symbolic parameter/return references resolvable through their defining scopes. Each root declaration restarts the traversal counter (one declaration's expansion cannot consume another's 4,096-visit budget; catalog bounds limit the root count).
    • Rebound-mutation coverage in tests/policy_artifact_checker.rs::private_absent_template_declarations_reject_rebound_universal_mutations: for a private never-instantiated struct template and a private absent function template at O0–O3, weakening a non-phantom parameter below the fixed-value boundary, repointing a symbolic field to Vec<T> (declared fixed ability no longer supported), and unresolved function parameter/return references reject exactly at the universal sweep after passing inspection; ability ordering and unresolved structural fields remain owned by the earlier catalog binding checks (asserted at their layer).
    • Validation: checker lib 40, open_handle_policy 9, interface_projection 8, interface_inspection 10, policy_artifact_checker 72, frozen_interface 38 all pass single-threaded; checker clippy --all-targets clean; fmt clean; commit gate passed. No producer metadata, wire schema or cache identity changed. Note: this commit is unsigned (--no-gpg-sign) because the agent's signing passphrase cache expired and interactive pinentry is unavailable in the current shell; branch history already contains unsigned commits.
    • Remaining H1 items: complete nominal qualification for ambiguous unqualified instantiation arguments in the interface-layer substitution/comparison path (interface/generics.rs — currently raw spellings are compared, so a template name or argument resolved differently per scope can collide textually), full directional receipt compatibility (effect weakening/binder relaxation), and the general codec/builder/executable obligations beyond policy-unit-scalars-flat-unsigned-cell-v1. Then H2 nominal handles and CKB-VM enforcement, builder/ProtocolBundle parity, final unified gates and the still-unassigned independent security review.
  5. a19q3 commented on Oct 10, 2026

    @a19q3
    CollaboratorAuthor

    Scoping result for the next H1 item — complete nominal qualification for ambiguous unqualified instantiation arguments (interface/generics.rs): the checker-side raw-spelling comparison (substitutions/matches_type/type_instances map template names and arguments as unqualified source text, so tokens resolved differently per scope can collide textually) is currently unreachable for the cross-module case, because the producer fails closed one step earlier. An imported template application used as a generic argument — e.g. consumer use a::Pair then Holder<Pair<u64>> — is rejected at compile time with E2111: type argument 'Pair__mono__…' for 'T' does not satisfy 'copy' (value-ability registry v1), with a single imported template and no name collision at all. Local template arguments compile and check; only imported ones lack registry evidence.

    Consequence: the qualification work splits into (1) a producer prerequisite — register ability evidence for imported template instances used as arguments, or intentionally document that boundary as unsupported — and (2) the checker-side two-scope qualification of type_arguments, declared field spellings and the instances map (qualify through the record/defining scopes as interface/layouts.rs already does for layout identities). Both remain open; no security hole exists today because the path fails closed at the producer. No code landed for this item in this session beyond the scoping evidence.

  6. a19q3 commented on Oct 10, 2026

    @a19q3
    CollaboratorAuthor

    Implementation progress at 972f978f on 0.32: the item-C producer prerequisite and the checker-side qualification both landed (issue remains open).

    • Producer prerequisite: src/generics.rs::register_external_items now derives omitted template value-abilities for imported struct/enum templates exactly as the local declaration path does (same derivation function, same definition, so owner and consumer derive the identical set). An imported template application can therefore serve as a generic argument to a locally declared template and compiles at O0–O3, with or without an explicit has clause — previously E2111: value-ability registry v1 rejected it fail-closed.
    • Checker qualification: when the declaration catalogs exist, the value-ability alias table registers each retained instance under its defining-owner identity plus record-scope-qualified arguments (qual::a::Pair<u64>), concrete type evidence answers both raw and scope-qualified spellings, and argument-constraint checks (bundle-level verify_instantiations and the interface-layer check_constraints) qualify unqualified names through the record module's nominal scope first. Two owners exporting the same template name now keep distinct qualified evidence: the two-owner fixture compiles, inspects, validates and projects with layout:qual::a::Pair<u64> distinct from layout:qual::b::Pair<u64>, and rebinding the local alias to the other owner is rejected by directional contract matching. The unqualified ambiguous spelling without catalogs, and any catalog-free bundle, stay fail-closed as before.
    • Known remaining boundary, recorded in the plan: an imported template instantiated under another imported template's arguments (doubly-external nesting) still fails closed at the owner seed boundary — the owner module has no evidence for a foreign monomorph name. Admitting it needs its own cross-module orchestrator contract (transitive seeds or owner-state evidence), not a registry patch.
    • Validation: compiler lib 997, policy_imports 6 (two new O0–O3 tests), policy_artifact_checker 72, interface_projection 8, interface_inspection 10, frozen_interface 38, checker lib 40 + open_handle_policy 9 — all single-threaded; clippy --all-targets/--lib clean for both crates; fmt clean; commit gate passed. Commit is unsigned (--no-gpg-sign, same GPG agent constraint as 8cfd1524).

    Remaining H1: full directional receipt compatibility (effect weakening/binder relaxation) and the general codec/builder/executable obligations beyond policy-unit-scalars-flat-unsigned-cell-v1, plus the doubly-external nesting orchestrator contract. Then H2, parity, final gates and the still-unassigned independent security review.

  7. a19q3 commented on Oct 10, 2026

    @a19q3
    CollaboratorAuthor

    Implementation progress at 49c7dd4c on 0.32: the directional receipt-compatibility relation landed (issue remains open).

    • CheckedModuleProjection::check_required_contracts (which the finite policy receipt also delegates to) now implements the enumerated directional relation instead of conservative exact equality: inferred effects (effective: contracts) and declared effects (callable: contracts) may only become more restrictive — Pure admits only Pure, ReadOnly admits Pure or ReadOnly, and each of Mutating/Creating/Destroying admits itself plus the two smaller classes, since the three state-changing classes are pairwise incomparable — and binder constraints (type_parameters on callables, parameters on generic template nominal contracts) may only relax: a candidate may stop demanding an ability the baseline demanded, so every baseline-admitted argument still satisfies it, but may never demand a new ability. Binder names, phantom flags, declared template abilities, codecs and every other field stay exact; absent required keys and any other difference still reject.
    • Tests: inferred_effects_may_only_become_more_restrictive (a Pure candidate satisfies a ReadOnly baseline; the reverse rejects) and binder_constraints_may_only_relax_directionally (a weaker phantom copy binder satisfies a copy + drop baseline and a relaxed T: copy callable satisfies T: fixed_value; both reverse directions reject) at O0–O3; the pre-existing strengthening mutation (copy → copy + drop as candidate) still rejects.
    • Validation: interface_projection 9, policy_artifact_checker 72, interface_inspection 10, policy_imports 6, frozen_interface 38, checker lib 40 + open_handle_policy 9 — all single-threaded; clippy --all-targets clean; fmt clean; commit gate passed. Commit unsigned (--no-gpg-sign, same GPG agent constraint as before).

    Remaining before #28 can close: the general codec/builder/executable obligations beyond policy-unit-scalars-flat-unsigned-cell-v1, the doubly-external nested-instantiation orchestrator contract, then H2 nominal handles and CKB-VM enforcement, builder/ProtocolBundle byte parity, final unified gates, and the independent security review — the reviewer still needs to be assigned by the maintainer; the 2026-10-03 waiver does not cover this contract.

  8. a19q3 commented on Oct 10, 2026

    @a19q3
    CollaboratorAuthor

    Implementation progress at 510beec9 on 0.32: the doubly-external nested-instantiation orchestrator contract landed (issue remains open).

    • An imported template instantiated under another imported template's application (e.g. consumer imports Holder from x and Pair from a, then Holder<Pair<u64>>) previously failed closed with E2111 at the outer owner's seed boundary, because the owner state had no evidence for the foreign monomorph name. The seed now carries, for every argument materialized in a third module, its derived ability evidence plus the defining owner module and concrete name (ForeignSeedArgument); the outer owner pre-registers that evidence before validating the seed and records the cross-owner reference, and the orchestrator (src/lib.rs) emits a use-import binding the requesting spelling to the argument owner's public concrete — aliased when the local import spelling differs from the defining source name. The independent artifact checker re-derives every ability claim from the emitted bundle; the forwarded producer values unblock the producer pass only.
    • Test: imported_template_nested_under_imported_template_materializes_across_owners compiles the three-module fixture at O0–O3, asserts the qual::a::Pair and qual::x::Holder instantiations in the record, and passes independent inspection and module projection.
    • Validation: policy_imports 7, compiler lib 997, policy_artifact_checker 72, frozen_interface 38, interface_projection 9, interface_inspection 10, checker lib 40 + open_handle_policy 9 — all single-threaded; clippy --all-targets clean; fmt clean; commit gate passed. Commit unsigned (--no-gpg-sign, same GPG agent constraint).

    With this, the H1 leftover list from the 2026-10-07 review is complete except the general codec/builder/executable obligations beyond policy-unit-scalars-flat-unsigned-cell-v1. Remaining before #28 can close: that general-profile extension, then H2 nominal handles and CKB-VM enforcement, builder/ProtocolBundle byte parity, final unified gates, and the independent security review — the reviewer still needs maintainer assignment; the 2026-10-03 waiver does not cover this contract.

  9. a19q3 commented on Oct 10, 2026

    @a19q3
    CollaboratorAuthor

    Implementation progress at e70bb0bb on 0.32: the finite external-codec profile advanced from policy-unit-scalars-flat-unsigned-cell-v1 to policy-unit-scalars-nested-unsigned-cell-v1 (issue remains open).

    • Declaration-level codec obligations now cover composed layouts: a public layout field may name another retained ordinary struct layout, which must resolve uniquely, carry kind struct, and recursively satisfy the same unsigned obligations — contiguous offsets across nesting boundaries, matched aggregate widths, depth ≤ 8, total ≤ 512 bytes. Entry-bound Cell layouts stay flat by design: their ELF-level field materialization certification (fixed_cell_fields) is per direct scalar field. The receipt and codec records both advance the profile string; identities are content-derived, and no schema or identity domain changed.
    • Scoping result recorded with the change: retaining a nested public layout currently requires entry-body usage, and the struct-literal helper frame lies outside the certified entry-body shapes, so end-to-end admission of nested layouts stays fail-closed at the Cell-field certification — pinned as a regression test (nested_public_layouts_stay_pending_entry_body_certification, O0–O3). Public constants still reject: their types are proven but not their values, and an independent constant-value proof needs its own bounded evaluation contract. These two — nested field-materialization certification and constant-value proofs — are the remaining named pieces of the general codec obligations, now with precise boundaries instead of an undifferentiated "general profile" gap.
    • The nested obligations themselves are unit-checked in the checker crate (external_codec::tests): contiguous composition across two nesting levels, aggregate widths, Cell-path flatness under the same record, and rejections for nested width mutation, boundary offset drift, missing member layouts, the depth bound, and equal-width nominal substitution semantics (structural for the codec check; owners stay distinct at the module-contract layer).
    • Validation: checker lib 42, policy_artifact_checker 73, frozen_interface 38 — all single-threaded; clippy --all-targets clean; fmt clean; commit gate passed. Commit unsigned (--no-gpg-sign, same GPG agent constraint).

    H1 is now complete except the two precisely-bounded pieces above. Then H2 nominal handles and CKB-VM enforcement, builder/ProtocolBundle byte parity, final unified gates, and the independent security review — the reviewer still needs maintainer assignment; the 2026-10-03 waiver does not cover this contract.

  10. a19q3 commented on Oct 10, 2026

    @a19q3
    CollaboratorAuthor

    Implementation progress at 0e525780 on 0.32: nested field-materialization certification landed — the nested unsigned profile now admits end to end (issue remains open).

    • Constructing a nested layout in the entry body lowers to calls of the fixed memory-copy helper (__cellscript_memcpy_fixed, arguments src/destination/length in a0/a1/a2). The finite Cell-field certification previously rejected every such call: only the membership helper was admitted. Two pieces now close that gap: (1) the copy helper itself is frame-certified — owned instruction range with internal copy loops allowed, no calls, no writes to its return or stack registers, at least one return, no escaping or incoming interior flow; (2) every call site proves its destination: within the pre-call window, the only definition of the destination register is a stack-relative offset inside the caller's frame, the length is a positive constant ≤ 512 bytes, the destination range is disjoint from checked Cell data and the received Cell pointer, and no incoming control flow enters the argument window. Anything else fails closed with the existing V2420 boundary.
    • End-to-end test nested_public_layouts_admit_end_to_end_with_certified_frame_copies: the three-declaration fixture (resource + nested Inner + Outer with a nested member) compiles at O0–O3 and now passes the complete finite external codec — profile policy-unit-scalars-nested-unsigned-cell-v1, Outer width 16 — and a same-width nominal substitute yields a distinct codec identity. Machine-word mutations at every certified copy site (destination offset escaping the frame, unbounded length) pass structural checking and reject exactly at the codec boundary.
    • Validation: checker lib 42 + open_handle_policy 9, policy_artifact_checker 73, frozen_interface 38 — all single-threaded; clippy --all-targets clean; fmt clean; commit gate passed. Commit unsigned (--no-gpg-sign, same GPG agent constraint).

    The general codec obligations are now down to one precisely-bounded piece: public constant-value proofs (types proven, values not; needs its own bounded evaluation contract). After that: H2 nominal handles and CKB-VM enforcement, builder/ProtocolBundle byte parity, final unified gates, and the independent security review — the reviewer still needs maintainer assignment; the 2026-10-03 waiver does not cover this contract.

  11. a19q3 commented on Oct 10, 2026

    @a19q3
    CollaboratorAuthor

    Implementation progress at f6bfb150 on 0.32: public constant-value proofs landed — the last general codec obligation piece (issue remains open).

    • Producer: with the source contracts, bind_typed_semantics emits an optional cellscript-constant-value-catalog-v1 into typed semantics. Each exported constant whose initializer is closed over the bounded grammar — u64/bool literals and checked add/sub/mul/div/rem/and/or/xor/shl/shr over u64 — records its canonical type, folded value and the expression tree (src/typed_semantics/constants.rs). Initializers outside the grammar (references, strings, bytes, wider widths) are omitted and stay fail-closed.
    • Checker: constant_values::check_constant_values independently re-evaluates every recorded expression under depth-16 / step-1024 budgets with checked operations (overflow, zero divisors, undefined shifts, unknown operators reject), compares against the recorded value, enforces identity/spelling bounds and catalog-exactness (proven set equals the declared public set, no duplicates, no undeclared entries). The finite external codec admits proven constants into its record (declaration identity, type, value) and the policy receipt drops its blanket constant rejection: a 3 * 7 constant now passes the complete finite codec and receipt with proven value 21, while a String constant still fails closed — now one layer earlier, at the origin check, verified in frozen_interface.
    • Mutation tests (constant_value_catalog_rejects_rebound_mutations, O0–O3): recorded-value drift, expression tampering, division by zero, a 20-deep tree exceeding the depth budget (whose leaves still evaluate to the recorded value), catalog removal and undeclared entries all reject after hash rebinding.
    • Validation: compiler lib 997, checker lib 42 + open_handle_policy 9, policy_artifact_checker 74, frozen_interface 38, interface_projection 9, interface_inspection 10, policy_imports 7 — all single-threaded; clippy clean on both crates; fmt clean; commit gate passed. Commit unsigned (--no-gpg-sign, same GPG agent constraint).

    With this, the general codec/builder obligations named in the scoping comments are complete: nested unsigned public layouts with certified frame-copy materialization, and proven constant values, both end to end under policy-unit-scalars-nested-unsigned-cell-v1. H1 is done. Remaining before #28 can close: H2 (nominal ScriptHandle/VerifierHandle DSL surface and CKB-VM runtime enforcement — the multi-session engineering layer), generated-builder/ProtocolBundle byte parity, final unified gates, and the independent security review — the reviewer still needs maintainer assignment; the 2026-10-03 waiver does not cover this contract.

  12. a19q3 commented on Oct 10, 2026

    @a19q3
    CollaboratorAuthor

    Implementation progress at f85230fe on 0.32: the H2 layer began with its mandated first slice — the frozen interface-parameter decision plus fail-closed reservation of the handle class spellings (issue remains open).

    • The participants plan requires freezing the nominal interface-parameter mapping before any ScriptHandle<I>/VerifierHandle<I> source syntax is admitted. The design record now carries the frozen H2 first-slice decisions: (1) I denotes the resolver-owned checked defining-module interface identity and will be spelled only through an explicit resolver-bound marker introduced with the parser slice — never a local struct, raw Script, package name, producer flag or ambiguous unqualified marker; (2) the bounded encoding is the existing 656-byte selection and 32-member authorization-set identity, with no new runtime budgets; (3) ScriptHandle and VerifierHandle are distinct nominal classes whose Lock/Type/spawned-verifier role lives in the policy header, with no coercion or retyping move from Script/Hash/bytes and construction only through versioned helpers mirroring the exact-handle operand-flow jail; (4) the expected authorization root comes from committed current-Script code/args or a controlled state transition, never the same witness carrying the membership proof, and membership/selected-dependency checks execute on CKB-VM before any on-chain claim.
    • Both class spellings are now reserved and fail closed at three layers: type validation (any reference, applied or bare, rejects with a specific reserved-surface message), top-level declaration registration (user structs/enums/functions/templates cannot manufacture the names), and independent nominal-catalog verification (a hash-rebound catalog renaming a declaration to ScriptHandle rejects at inspection). Tests: four source-level rejection shapes at O0–O3 in tests/exact_script_handles.rs, plus the rebound catalog mutation in tests/policy_artifact_checker.rs.
    • Validation: compiler lib 997, checker lib 42, exact_script_handles 5, policy_artifact_checker (spot + full catalog tests), frozen_interface 38 — all single-threaded; clippy clean; fmt clean; commit gate passed. Commit unsigned (--no-gpg-sign, same GPG agent constraint).

    Next H2 slices in order: the parser/resolver marker grammar for I (imported module interface designation), then the nominal handle types with bounded encoding and no construction (type surface only), then versioned construction helpers over the checked admission boundary, then the CKB-VM membership/selected-dependency runtime checks with measured budgets, each with parser/type/lowering/metadata/checker/LSP/formatter agreement. The independent security reviewer still needs maintainer assignment; the 2026-10-03 waiver does not cover this contract.

  13. a19q3 commented on Oct 10, 2026

    @a19q3
    CollaboratorAuthor

    Implementation progress at d7fc4a6e on 0.32: the H2 marker slice landed — the interface parameter I binds to imported modules per the frozen decisions (issue remains open).

    • Grammar and producer validation: an interface designation is a plain module path in the handle's single type argument (ScriptHandle<owner::module> / VerifierHandle<owner::module>). validate_open_handle_type admits exactly one argument that is neither a primitive, a value-type spelling, a locally visible type, the current module, a nested application nor an unloaded path — the project resolver must have the designated module loaded. Bare spellings and user declarations of the two class names keep the reserved-surface rejection.
    • Value positions fail closed: a shared recursive check rejects handle leaves in struct/enum fields and callable parameters (identity-only until the runtime encoding slice), while generic application arguments — the identity position — are admitted; the wrapper-with-phantom-parameter fixture compiles at O0–O3, records the ScriptHandle<qual::types> argument in the instantiation, and passes independent inspection plus symbolic declaration validation.
    • Checker agreement: any handle leaf in the typed record (field leaves and generic arguments alike) must name a module of the checked source closure — a nominal-catalog scope, and never the record's own module — before receiving its identity-only copy/drop abilities; layout template parameters never accept a designation. A hash-rebound designation swap is caught by the canonical-identity layer, and the designation scope check stands behind any self-consistent rewrite.
    • Validation: compiler lib 997, checker lib 42, policy_imports 10 (three new tests: positive binding, six-shape rejection matrix, designation mutation), exact_script_handles 5 (reservation matrix updated to the new structured messages), interface_projection 9, interface_inspection 10, policy_artifact_checker 75, frozen_interface 38 — all single-threaded; clippy clean on both crates; fmt clean; commit gate passed. Commit unsigned (--no-gpg-sign, same GPG agent constraint).

    Next H2 slices: bounded runtime encoding and value construction through versioned helpers over the checked admission boundary, then CKB-VM membership/selected-dependency enforcement with measured budgets. The independent security reviewer still needs maintainer assignment; the 2026-10-03 waiver does not cover this contract.

  14. a19q3 commented on Oct 10, 2026

    @a19q3
    CollaboratorAuthor

    Implementation progress at fb7a02c2 on 0.32: the H2 bounded-encoding slice landed — open-handle values now cross the action witness ABI with their frozen 656-byte width (issue remains open).

    • An action witness parameter of ScriptHandle<I>/VerifierHandle<I> type is the entry-ABI position for the bounded encoding. The 656-byte width is now carried consistently through every layer that previously classified the handle as schema-dynamic: the producer's string-based and IR-based width tables, the entry witness payload layout (fixed-byte pointer/length ABI instead of a 4-byte schema cursor), the action parameter metadata (fixed_byte_len: 656), and — independently — the checker's policy-ABI projection and adapter-frame expectation. Producer and checker now derive the same compact policy adapter frame for a 656-byte handle parameter; the earlier divergence (producer fallback frame 5376 vs checker 688) is closed at the root. Universal symbolic verification recognizes the handle leaf (single module designation, copy/drop only), and the value-ability prescan validates designations on entry parameters like every other handle leaf.
    • Consumption stays jailed: no versioned helper exists yet, so the checker's operand-flow jail rejects any operation touching a handle operand — the value crosses the ABI but cannot be used in a body until the runtime helper ships with membership and selected-dependency enforcement. Non-witness parameter kinds, functions and locks remain identity-only.
    • Tests: open_handle_witness_parameters_carry_the_bounded_encoding — the three-module fixture compiles at O0–O3 with the handle witness parameter, metadata records the 656-byte fixed encoding, independent inspection and symbolic validation pass, and an input-sourced handle parameter rejects.
    • Validation: compiler lib 997, checker lib 42 + open_handle_policy 9, policy_imports 11, exact_script_handles 5, policy_artifact_checker 75, frozen_interface 38, interface_projection 9 — all single-threaded; clippy clean on both crates; fmt clean; commit gate passed. Commit unsigned (--no-gpg-sign, same GPG agent constraint).

    Next H2 slices: the versioned construction/consumption helper over the checked admission boundary, then CKB-VM membership/selected-dependency enforcement with measured cycle/stack/witness budgets. The independent security reviewer still needs maintainer assignment — that unassigned human review remains the final closure gate; the 2026-10-03 waiver does not cover this contract.

  15. a19q3 commented on Oct 10, 2026

    @a19q3
    CollaboratorAuthor

    Scoping result for the next H2 slice — the versioned consumption helper (issue remains open).

    The slice's implementation contract is now frozen in the design record (commit da187ad2, "H2 versioned helper specification"): the DSL surface ckb::require_cell_dep_open_script_handle(dep: CellDepView, handle: ScriptHandle<I>, expected_root: Hash) (plus the spawned-verifier variant), the complete offset map of the 656-byte selection wire (header at 8 with member_count at 11, member at 196 with complete_script at 316, canonical index at 488, five sibling hashes at 496), and the three on-chain steps — structural validation, full Merkle membership recomputation over __ckb_hash_blake2b_var with the frozen domain strings, and the selected-dependency Script-hash comparison against the dep's lock/type hash — plus the exact layer checklist (typecheck arm, lowering target, calls.rs loading, the runtime helper generator reusing the exact-handle frame skeleton, checker jail exemption with signature checks, a new runtime error code, LSP, and O0–O3 CKB-VM positives/negatives).

    Two implementation facts discovered while scoping: the on-chain runtime already provides __ckb_hash_blake2b_var, so full membership verification needs no new hash primitive; and the exact-handle requirement helper is a directly reusable frame/syscall skeleton. The remaining work is a hand-written RISC-V helper generator (~200–300 lines of emitters with frame-resident hash staging buffers) plus the four-layer pipeline and CKB-VM evidence. That hand-written assembly is exactly the class of change the repository warns must not be rushed — a single wrong register or label silently changes on-chain code — so it is deliberately not attempted in the tail of this session; it is the first item of the next session, with the specification removing all design ambiguity.

    Session totals for #28 so far: 13 commits (H1 complete, H2 decisions/markers/encoding plus this specification), 14 progress comments. Remaining after the helper: CKB-VM enforcement evidence with measured budgets, builder/ProtocolBundle byte parity, final unified gates, and the independent security review — the reviewer still needs maintainer assignment; the 2026-10-03 waiver does not cover this contract.

  16. a19q3 commented on Oct 10, 2026

    @a19q3
    CollaboratorAuthor

    Implementation progress at 2d49a058 on 0.32: the versioned consumption helper landed through the complete pipeline — with one tracked byte-level anomaly in the generated assembly (issue remains open).

    Landed per the frozen specification (da187ad):

    • Typecheck arms for ckb::require_cell_dep_open_script_handle / ..._open_verifier_handle beside the exact handles (three-argument shape, class-matched handle, compile-time Hash root); lowering to versioned __ckb_ targets requiring a constant expected root; calls.rs argument loading for the 656-byte fixed-byte selection and 32-byte root; the runtime helper generator implementing structural validation (magic, class, role, active status, index < member_count), five-level Merkle membership recomputation over __ckb_hash_blake2b_var with the frozen domain strings, and the selected-dependency binding of the member's complete Script hash (offset 324) against the dep's lock/type or data hash by the header role; the checker operand-flow jail now admits exactly these two targets with pinned signatures (versioned runtime contract, unit return, constant root commitment); runtime error code 85 open-handle-invalid with registry, slug, description and hint; LSP completion entries; and selection-wire layout constants shared by tests.
    • Two real bugs were found and fixed during CKB-VM bring-up: the member complete-Script offset in the frozen spec was wrong (316 → corrected to 324, member offset 128), and the entry wrapper classified the 656-byte handle parameter as schema-dynamic because the IR pointer-width table lacked a size-independent arm — both producer width tables now carry the frozen 656 consistently. All far conditional branches in the long helper route through near-label trampoles since the assembler performs no branch relaxation.
    • Remaining tracked anomaly: the CKB-VM positive case still fails closed with 85 — one structural byte read inside the helper returns an unexpected value even though the wire is self-consistent under the authority recompute, the wrapper now hands the correct fixed 656-byte pointer, and the magic bytes at that pointer verify. The bisect state is preserved as an #[ignore]-marked test (open_script_handle_requirement_executes_and_rejects_substitutions) with the full positive/negative matrix ready; the ignore note names the anomaly so the next session resumes exactly here.
    • Validation of what is asserted: compiler lib 997, checker lib 42, policy_imports 11, exact_script_handles 5 (+1 tracked ignored), clippy clean on both crates, fmt clean, commit gate passed. Commit unsigned (--no-gpg-sign, same GPG agent constraint).

    The independent security reviewer still needs maintainer assignment — that unassigned human review remains the final closure gate; the 2026-10-03 waiver does not cover this contract.

  17. a19q3 commented on Oct 10, 2026

    @a19q3
    CollaboratorAuthor

    Implementation progress at 0b5b31f7 on 0.32: the tracked byte-level anomaly is found and fixed — the open-handle requirement helper now passes its complete CKB-VM acceptance matrix (issue remains open).

    Root causes (three real defects in the generated helper, all caught by the VM evidence):

    1. Header field offsets: the selection wire nests the 188-byte header block (which carries its own CSOHPv1\0 magic) at selection offset 8, so class/role/mode/member_count sit at absolute 16/17/18/19 — the original constants used the in-header positions. The same nesting applies to the member block (CSOHMv1\0), so its status byte sits at 204, not 196. The frozen specification's offset table and the shared layout constants are corrected.
    2. __cellscript_memcpy_fixed argument direction: the runtime copies a0→a1, and every staging copy in the Merkle recomputation (leaf member, both sibling orders per level, policy header and tree root) had source and destination swapped — hashing frame garbage. All call sites now pass source in a0 and destination in a1.
    3. A test-loop upper bound (opt 4 does not exist).

    Acceptance now green in open_script_handle_requirement_executes_and_rejects_substitutions (unignored), at O0–O3: the admitted selection verifies (exit 0); a substituted expected root, byte substitutions at nine selection offsets (selection magic, class, count, member magic, complete Script hash, canonical index, first sibling, last byte) and a substituted CellDep Script all fail closed with open-handle-invalid (85).

    Validation: compiler lib 997, checker lib 42, policy_imports 11, policy_artifact_checker 75, frozen_interface 38, exact_script_handles 6 (matrix included); clippy clean; fmt clean; commit gate passed. Commit unsigned (--no-gpg-sign, same GPG agent constraint).

    Remaining before #28 closes: measured cycle/stack/witness budgets for the helper, generated-builder/ProtocolBundle byte parity, the expected-root binding to committed current-Script code/args (frozen decision #4's follow-up), final unified gates, and the independent security review — the reviewer still needs maintainer assignment with a time window; the 2026-10-03 waiver does not cover this contract.

  18. a19q3 commented on Oct 10, 2026

    @a19q3
    CollaboratorAuthor

    Implementation progress at ca140501 on 0.32: the helper's measured budgets landed and the from-args root binding advanced through its complete pipeline (issue remains open).

    Measured budgets (frozen as test assertions, so regressions fail the suite):

    • Open-handle verification action: 76,181 cycles at O0 and 76,176 at O1–O3, frozen ceiling 3,000,000 cycles — ~2.5% of the ceiling, leaving the policy budget intact.
    • Witness payload: fixed 664 bytes (8-byte entry ABI magic + the 656-byte selection), asserted unchanged.
    • Frame: helper 848 bytes over the entry wrapper's 5,376-byte frame.
      The numbers are recorded in the design record beside the acceptance matrix.

    From-args root binding (frozen decision #4: the expected authorization root comes from the current Script's committed args, never from the witness):

    • ckb::require_cell_dep_open_script_handle_from_args(dep, handle) and the verifier variant are implemented through every layer — typecheck arms (two-argument shape, class-matched handle), lowering to versioned targets, calls.rs loading, a runtime generator variant that LOAD_SCRIPTs the current Script, requires its serialized args field to be exactly 32 bytes and stages the committed bytes as the expected root, checker jail admission with pinned two-operand signatures, and LSP entries.
    • The prologue is live-verified in the VM: LOAD_SCRIPT returns the 85-byte committed Script, the staged root pointer reads the committed args byte-for-byte, and the reloaded selection pointer and length verify ('C' at byte 0, 'W' at byte 4, length 656).
    • One control-flow defect remains: despite those verified inputs, the helper still fails closed with open-handle-invalid before the membership recomputation. Rather than ship an unexplained success path, the complete matrix (committed-root positive, substituted root, 31/33-byte args, seven selection substitutions) is preserved as an ignore-marked test with this context, and the constant-root variant remains the fully admitted surface. The defect is the next session's first item; the bisect state (verified prologue, failing between the structural checks and the leaf hash) is documented in the design record.

    Validation: compiler lib 997, checker lib 42, policy_imports 11, exact_script_handles 6 (+1 tracked ignored), clippy clean, fmt clean, commit gate passed. Commit unsigned (--no-gpg-sign, same GPG agent constraint).

    The independent security reviewer still needs maintainer assignment with a time window — the final closure gate; the 2026-10-03 waiver does not cover this contract.

  19. a19q3 commented on Oct 10, 2026

    @a19q3
    CollaboratorAuthor

    Debugging record for the from-args fail-closed path (no new commit; working tree restored to ca140501 and re-gated).

    The binary search narrowed further, and one earlier hypothesis was eliminated with a live experiment:

    • Not the disabled-helper stub: the stub's return code was temporarily changed to 86 and the fixture still exited 85 — the enabled path is live.
    • The prologue exists and runs in the real target ELF: the target-compiled helper block was decoded through the checker's own decoder (15 machine words: frame setup, VIEW/length staging, LOAD_SCRIPT ecall, first near-trampoline branch) — byte-for-byte matching the metadata-only assembly.
    • The reload block executes and its outputs verify live: selection pointer ('C', 'W') and length (656) were confirmed by register dumps in prior turns.
    • The failing region is therefore confined to the three near-trampoline checks between the reload and ld t3 (beqz a1 / length equality) plus the magic loop's first comparison — all of which emit the same instruction shapes that pass in the constant-root variant. A metadata-only dump cannot distinguish further, and compile-time probes (inline li a0,N; ret) are rejected by the verifier's stack-delta and failure-record contracts.

    Recommended next step (recorded for the next session): reproduce the fixture in ckb-debugger and single-step the helper from the LOAD_SCRIPT return — the failing branch target will be visible directly, which the compile-time bisect cannot show.

    State: constant-root variant fully green (76k cycles, full substitution matrix), budgets recorded, from-args pipeline complete through every layer with the prologue live-verified; the one unresolved branch keeps its ignore-marked test with the full matrix. Compiler lib 997 / checker 42 / exact_script_handles 6+1 / commit gate re-verified on the restored tree.

    The independent security reviewer still needs maintainer assignment with a time window — the final closure gate for #28; the 2026-10-03 waiver does not cover this contract.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions