Repository navigation
Conversation
…very fork, and keeps retired tokens local A broker-owned unpin the node refuses leaves blocks pinned that no session holds, so the shared follow now stops for every follower instead of growing retention unseen; a release still in flight when its follow ends stops nothing. A session joining a shared follow late is replayed every live fork, not only the best block's ancestors, so a later best switch never names a block it has not heard of. A follow token the broker retired is never forwarded to the node, and stopping an operation under it answers success.
…inline bytes first The importer nests large files under intermediate dag-pb stem nodes. A stem was read as its (usually empty) inline data, so every file beyond one link level loaded as zero bytes; stems now recurse into their links. A file node's own inline bytes are kept ahead of its linked chunks instead of being dropped.
…mmutable cache serves them `?format=car` already selects the CAR representation. The extra `Accept: application/vnd.ipld.car` header made the gateway bypass its immutable-content cache for the same archive, so every load went to origin. Requested-CID and CAR-block verification are unchanged.
…tures from module loading Provider discovery coverage: a child block no connected peer had yet is fetched on a later attempt after its root succeeded, concurrent retries get their own answers, and a block no peer serves fails at the total retry budget. The block-cache fixtures finish loading the relay module before a case installs one, so an import that outlives a timed-out case can no longer install its listener after teardown.
…one, and a follow that stopped before caching is rebuilt Both resolution backends now cache a client before subscribing to its stop, check that the stop belongs to the client still cached, and clear their references before tearing a client down. A late stop from a follow that was already replaced no longer destroys its replacement, and a follow that stopped after it became ready but before it was cached fails the attempt so the resolver rebuilds instead of reading through it. An RPC connection abandoned by a network switch can no longer clear its replacement's pending connection and make the next resolve open a duplicate client.
…unpin fails its reads as stopped Destroying the raw API now ends it like a stop: it notifies its owner once, rejects readiness, and fails every read still in flight with `ApiStoppedError` instead of leaving it to papi's `DisjointError` or a transport that may never answer. A block the node refuses to unpin ends the follow, so its owner redials rather than retaining history nothing will release, and a follow initialized without a finalized block stops at once. The new tests drive the installed substrate client against a scripted chainHead server, directly and through the protocol broker, and check that no block is unpinned while a read still uses it.
Contributor
Bundle Size ReportEager path (module entry + modulepreloaded chunks):
Chunks over 500 KB:
All files
Commit: bbd4689 |
Contributor
⚡ Performance ReportCold StartOverall: 🔴 regressed | p50: 10.72s → 11.82s (+10.3%) | p95: 11.79s → 12.30s (+4.4%) | Mann-Whitney: z=3.48 ✅ significant Phase breakdown
Warm StartOverall: 🔴 regressed | p50: 424ms → 435ms (+2.6%) | p95: 451ms → 472ms (+4.7%) | Mann-Whitney: z=3.06 ✅ significant Phase breakdown
Lukewarm Start (different site, same session)Overall: 🔴 regressed | p50: 13.82s → 14.51s (+5.0%) | p95: 14.69s → 15.38s (+4.7%) | Mann-Whitney: z=2.60 ✅ significant Phase breakdown
Commit: bbd4689 | Outliers (>2x best) discarded before stats |
…itted them The broker did not own author_submitAndWatchExtrinsic subscriptions, so their author_extrinsicUpdate events arrived for an unknown token and were dropped. The local wallet's core registers its allowances through that legacy watch, so the registration never completed. The broker now owns and releases those subscriptions, and the watch guard ends them with a legacy dropped update when the transport disconnects.
replghost
marked this pull request as ready for review
October 12, 2026 19:32
This branch has not been deployed
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.
Summary
Generic chain-transport, resolver and content-delivery fixes, taken out of the PolkaVM runtime PR (#185) so they can land on their own. Nothing here depends on PolkaVM, the vendored host SDK, notifications or the sandbox contract.
Fixes
Protocol broker (
packages/protocol/src/broker.ts)chainHead_v1_unpinfails, blocks stay pinned that no session holds, and the node may also have dropped them. The broker now unfollows upstream and sends every followerstop, so their owners follow again. A release still in flight when its follow ends is forgotten, so its late reply cannot stop the next follow.bestBlockChangedto another fork then named a block that session had never heard of. Now every live block is replayed, parents before children.chainHead_v1_stopOperationsucceeds, since there is nothing left to stop, and anything else fails withUnknown subscription/token.author_submitAndWatchExtrinsicsubscriptions, so theirauthor_extrinsicUpdateevents arrived for an unknown token and were dropped. The local signer from feat(ui): use a Polkadot App account locally from the debug panel #362 registers its allowances through that watch, so registration never completed on main. The broker now owns and releases these subscriptions, and the watch guard ends them with a legacydroppedupdate when the transport disconnects (packages/protocol/src/watch-guard.ts).Resolver (
packages/resolver)destroy()now ends the raw API the same way a stop does: it notifies its owner once, rejectswhenReady(), and fails every read still in flight withApiStoppedError. Before, those reads were left with papi'sDisjointError, or hung on a dead transport. A refused unpin ends the follow instead of keeping history that nothing will release, and a follow initialized without a finalized block stops at once.Content (
packages/content)?format=caralready selects the CAR representation. The extraAccept: application/vnd.ipld.carheader made the gateway skip its immutable-content cache, so every load went back to origin. Requested-CID and CAR-block verification are unchanged.Relationship to #359
#359 changes how
withBestBlockretries reads the node reports as inaccessible: it restarts the whole read at the current head. This PR keeps main's pinned-read design and leaves that retry alone. The two touch different lines ofapi.ts, merge without conflicts, and the merged resolver suite passes.Not included
withContract) is left out. Main'swithBestBlockpinning (fix(resolver): reads survive light-client resets while catching up, and the resolver unpins blocks #320) already bounds pins to live reads, and fix(resolver): restart inaccessible reads at the current head #359 builds on it. This PR adds the stop and unpin guarantees that feat(polkavm): run App Manifest v2 runtimes in Dotli #185's version had and main's lacked.Verification
npm run typecheck,npm run lint,npm run format:check,npm test(all packages)api.ts, and 2 fail on main's broker.VITE_NETWORKS=paseo-next-v2,previewnet VITE_METRICS=true VITE_SENTRY_DSN=…) and the functional suite with the CI threshold: 81 specs, 68 passed, 13 skipped (navigation and host-settings cases that skip themselves; none in network-transport), 0 failed in every gated file.