paritytech / paritytech/contract-dependency-manager
cdm deploy races registration against the Bulletin publish — a publish failure leaves the package registered but permanently uninstallable
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 4
- Forks
- 3
- Avg merge
- 4d 13h
- Merged PRs (30d)
- 3
Description
Summary
A Bulletin metadata publish failure during cdm deploy leaves the package registered but permanently uninstallable, and retrying makes it worse: the retry deploys a fresh contract set at the next version, orphaning the first. Hit twice, identically, deploying three contracts to -n paseo with cdm 0.13.0.
[Bulletin publish item 1/3] Metadata publish failed (metadata-0):
[Bulletin publish] Transaction timed out after 300s.
The transaction may still be processing on-chain.
After the failure:
- the contracts are instantiated and are registered —
getVersionCountincremented,getAddress/getMetadataUrireturn the new address and CID; cdm deployexits non-zero andcdm.jsonis never written;cdm installresolves address + CID from the registry, then dies onIPFS fetch failed: Gateway Timeout— the gateway 504s exactly the new CIDs (an old CID: HTTP 200 in 0.49s; the new ones: 504, polled every 20s for 4 minutes). The bytes never reached the Bulletin chain, so nothing can serve them.
Root cause (cdm side)
src/lib/contracts/src/pipeline.ts submits the two chain writes concurrently:
const [deployRes, publishRes] = await Promise.all([
deployer.deployAndRegisterBatch(..., metadataUris, ...), // Asset Hub: instantiate + registry publishLatest(CID)
publisher.publishBatch(metadataList), // Bulletin: TransactionStorage.store
]);
The CID is precomputed locally and the Asset Hub batch registers it atomically with the instantiate — so by the time the Bulletin store times out, the registry already points at content that doesn't exist. The Promise.all rejection can only abort the pipeline after the damage is done.
Recovery is then impossible by design:
- the metadata embeds
published_at: new Date().toISOString(), so a re-run produces different bytes → a different CID — the bytes hashing to the registered CID only ever existed in memory and are gone; - there is no
publish the metadata I already registeredpath; - a retried deploy reads the bumped
getVersionCount, derives a new CREATE2 salt, and instantiates a second contract set — we now have an orphaned v0 and a broken v1 on paseo.
(Why Bulletin never included a validly broadcast TransactionStorage.store within 300s — twice — is a separate chain-side question; allowance was healthy: 100 txns, 40.9 MB. This issue is about cdm turning that transient failure into a permanent one.)
Fix
Sequence the writes: publish to Bulletin, verify CIDs, then deploy+register. This restores the invariant registered ⇒ fetchable. A failed publish aborts before anything is registered, so the deploy is safely retryable; a deploy failure after a successful publish leaves only harmless orphaned Bulletin content.
Implemented and validated against a real deploy; PR to follow.
Follow-ups worth considering (not in the PR)
- Persist the metadata bytes under
.cdm/(keyed by CID + package/version) before submitting, so an interrupted run leaves a re-submittable artifact. cdm publish-metadata <package>— re-submit persisted bytes whose CID matches the registry'sgetMetadataUri, as a repair path for states like the current paseo one.- Surface the tx hash in
TxTimeoutErrorreporting (product-sdk-tx logs it, the error doesn't carry it), so "may still be processing" is checkable.
Environment
- cdm 0.13.0, bun 1.2.23, macOS arm64
- chain:
paseopreset; registry0xc1a73a4f93fde65b1cb1680baead248073566cb0 - gateway:
https://paseo-bulletin-next-ipfs.polkadot.io/ipfs/ - affected packages:
@w3s/playground-registry,@w3s/playground-identity,@w3s/playground-open-verifier(v0 orphaned + v1 registered-but-unfetchable)
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in src/lib/contracts/src/pipeline.ts and trace the cdm deploy flow around the concurrent deployAndRegisterBatch and publishBatch calls. Verify the failure and retry behavior, then validate a deploy against the affected chain. Done means Bulletin content is available before registry registration, and a publish failure leaves deployment safely retryable.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- blockchain, cli
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 35/100