feat(registry)!: register an existing global contract by code hash (ENG-631) - #596
Draft
peer2f00l wants to merge 1 commit into
Draft
Conversation
…NG-631)
`add_version` took `(DeployMode, code)` — two parameters encoding one decision,
admitting a combination that means nothing (`Normal` alongside bytes already
published globally) and offering no way to point a version key at a global
contract already on chain. A second registry serving the same code therefore
paid the global-contract storage stake a second time: 65+ NEAR for the
universal-account wasm, to publish bytes the protocol already stores under that
exact hash.
Fold the pair into one source:
VersionSource::Stored(code) = 0
VersionSource::PublishGlobal(code) = 1
VersionSource::ExistingGlobal(hash) = 2
Discriminants 0 and 1 match `DeployMode::Normal`/`GlobalHash`, and `Base64VecU8`
is a transparent borsh newtype over `Vec<u8>`, so the encoding is byte-identical
for both existing modes and the 1.1.0+ registries already deployed keep working
untouched. That equality is pinned by a golden-bytes test rather than trusted,
and the `live_release_migrates_onto_current` cases exercise the 0.1.0, 1.0.0 and
1.1.0 registries end to end. Pre-1.1.0 predates the tag entirely and still takes
a bare `(version_key, code)`; a code hash has no representation there at all, so
that combination is an error rather than an encoding the registry would misread
as a wasm blob.
The hash is verified, not trusted. `ExistingGlobal` inserts the entry, then a
create/`use_global_contract`/delete receipt on `probe.<registry>` proves a global
contract stands behind it; a hash with nothing behind it fails the receipt and
the existing `add_version_01_finalize` frees the key again. This is what makes an
unverified typo unreachable — `remove_version` hard-panics on a `GlobalHash`
entry, so a burned key would stay burned. The probe uses a name distinct from
`deploy.<registry>` so it cannot collide with an in-flight publish, and both
paths now share one scratch-account helper.
The probe takes one yoctoNEAR, measured rather than estimated. The account is
created and deleted inside a single receipt, so it never has to satisfy a
storage-staking minimum, and `use_global_contract` copies no code to stake for;
sandbox confirms the receipt succeeds at 1 yocto, against ~50 NEAR to publish the
same market wasm. The cost here is gas.
`DeployMode` remains for `registry.addArtifactVersion`, where the bytes are
resolved from the catalog and only store-versus-publish is open.
The gateway cannot yet reject `ExistingGlobal` against a registry too old to
parse it: that check keys on the version number of the release carrying this
change, which does not exist until it ships. Such a registry fails on chain
rather than at preflight until then; the preflight check follows in a stacked
change once this is released.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
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.
Closes ENG-631. Unblocks ENG-553 (staging UA registry).
add_versiontook(DeployMode, code)— two parameters encoding one decision, admitting a combination that means nothing (Normalalongside bytes already published globally) and offering no way to point a version key at a global contract already on chain. A second registry serving the same code paid the global-contract storage stake a second time: 65+ NEAR for the universal-account wasm, to publish bytes the protocol already stores under that exact hash.The fold
Discriminants 0 and 1 match
DeployMode::Normal/GlobalHash, andBase64VecU8is a transparent borsh newtype overVec<u8>, so the encoding is byte-identical for both existing modes — the 1.1.0+ registries already deployed keep working untouched.That equality is pinned, not trusted:
borsh_is_wire_compatible_with_deploy_modeasserts it directly against the old tuples, and the threelive_release_migrates_onto_currentsandbox cases drive real 0.1.0/1.0.0/1.1.0 registries end to end. Pre-1.1.0 predates the tag entirely and still takes a bare(version_key, code); a code hash has no representation there at all, so that combination errors rather than encoding into something the registry would misread as a wasm blob.The hash is verified, not trusted
ExistingGlobalinserts the entry, then a create/use_global_contract/delete receipt onprobe.<registry>proves a global contract stands behind it. A hash with nothing behind it fails the receipt and the existingadd_version_01_finalizefrees the key again.This is what makes an unverified typo unreachable:
remove_versionhard-panics on aGlobalHashentry, so without the probe one bad hash would burn a version key permanently. The probe uses a name distinct fromdeploy.<registry>so it cannot collide with an in-flight publish; both paths now share one scratch-account helper.The probe costs one yoctoNEAR — measured, not estimated
The issue anticipated measuring an account-creation floor and requiring a deposit covering it. Measured in sandbox, the floor is 1 yocto: the probe account is created and deleted inside a single receipt, so it never has to satisfy a storage-staking minimum, and
use_global_contractcopies no code to stake for. The cost is gas.I built the constant first, then deleted it once the measurement came back — a deposit the chain does not require would have been invented machinery.
assert_one_yocto()is the contract's existing idiom for exactly this. Against ~50 NEAR to publish the same market wasm, the ratio assertion indeployment.rsis now> 1_000_000x.Two deliberate deviations
registry.addArtifactVersionkeepsDeployMode. The issue saysDeployModeretires from "the gateway spec"; I read that as scoped toregistry::AddVersion, where the invalid combination lived. The artifacts path resolves its own catalog-verified bytes, soExistingGlobalis meaningless there and only store-versus-publish is open. Say the word if you meant a full retirement.No preflight version gate yet, and no version bump. Rejecting
ExistingGlobalagainst a registry too old to parse it keys on the version number of the release carrying this change — which does not exist until it ships. Such a registry fails on chain rather than at preflight until then. The check is prepared and stacked: branch 2, to merge only after this is released.Acceptance criteria
Stored/PublishGlobalencode byte-identically to theDeployModetuplesuse_global_contract, identical to a published one, for a deposit orders of magnitude belowget_version_code_hashisNone, key is reusableregistry.deploytools/managerunit tests for the code-hash sourcegateway/METHODS.mdregenerated — no diff, as the reference renders method names rather than field shapesVerification
61/61sandbox (registry + gateway),665fast tests, relayer11/11,cargo fmt --all --checkclean, and CI'scargo clippy --all-features --workspace --tests -- -D warningsclean.Note for review
The three-variant gateway test reads its hash through a raw
get_version_code_hashview rather thanregistry.getVersion, because the pre-existing ENG-559/560 gate putsgetVersionbehind ≥1.3.0 — which no deployed or sandbox registry satisfies yet. Worth a separate look: that gate makes severalregistry.*reads unusable against every registry currently in existence.🤖 Generated with Claude Code
This change is