Repository navigation
feat: add native beacon networking - #782
Draft
wemeetagain wants to merge 784 commits into
Draft
wemeetagain wants to merge 784 commits into
wemeetagain wants to merge 784 commits into
Conversation
The musl targets failed to compile and then to link the addon. - `binding.SendInfo` spells out `quiche_send_info`, which translate-c makes opaque for musl because musl's `struct timespec` pads with bitfields; `binding.connSend` passes it to quiche. - `build.zig` builds snappy's static library position independent, which snappy.zig leaves to the target default, off for musl, although the addon links it into a shared library. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GrHAFF8ZQVeLixLgm3YWBr
The packaged-install probe only loaded the host's addon, so no target's packed packages ran a runtime lifecycle. - `check_network_package.mjs` installs the packed main and platform packages for any `--target` into a consumer; `--lifecycle` adds the existing fixtures, with `bindings/src` resolving to the installed package, and their dependencies at this checkout's versions. - Without `--consumer` it runs the checks on this host; with it, the consumer stays for the target's platform or container to run. - `network-package-probe.mjs` checks that the addon loads from the platform package for the running platform, and that blst and shuffle work around sequential worker loads. - `network-package.mjs` runs the probe and, with `--lifecycle`, worker-only loads, worker termination with outstanding publications, requests and incoming, incoming serving, close with every family outstanding, reinitialization, facade collection, normal close and shutdown failure, each bounded. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GrHAFF8ZQVeLixLgm3YWBr
CI built the six targets once on Ubuntu and ran only the host's load probe, and each release rebuilt them without running any. - `bindings-platforms.yml` builds all six ReleaseSafe artifacts through `scripts/zig-cross.sh` on the pinned Ubuntu 22.04 release image, then runs the packaged lifecycle checks per target: glibc on Debian 11 (glibc 2.31), musl on Alpine, both on x64 and arm64 runners, and Darwin on Intel and Apple silicon macOS runners. - CI's `bindings-platforms` replaces `bindings-artifacts`. - The Release workflow runs it once, and the dev and release publishing jobs publish its artifacts instead of rebuilding. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GrHAFF8ZQVeLixLgm3YWBr
…rget `binding.SendInfo` was checked against translate-c's struct only where it is not opaque, so musl, the target it exists for, went unchecked. - `send_info_layout.h` states the C compiler's size, alignment and field offsets of `quiche_send_info` and its `timespec` as enum constants, which `build.zig` translates for the network module's target. - `binding.zig` asserts at compile time that `SendInfo` matches every one of them; a swapped `timespec` fails the musl, glibc and native builds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GrHAFF8ZQVeLixLgm3YWBr
Release builds used whatever Rust the runner had and let Cargo resolve quiche's dependencies anew. - `rust-toolchain.toml` pins Rust 1.91.1 with `rust-src`, whose standard library notices the package provenance check compares, for every Cargo build in the tree. - `scripts/zig-cross.sh` puts a `cargo` first on the path that adds `--locked` to builds, so a release build fails rather than change quiche's `Cargo.lock`. quiche-zig should pass `--locked` itself. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GrHAFF8ZQVeLixLgm3YWBr
The probe resolved on a worker's "ok" and discarded its exit code, so a worker that reported "ok" and exited 7 passed. - `runWorker` settles at the worker's exit and passes only when the worker raised nothing, reported "ok" alone and exited with 0. - `--worker-exit=CODE` has the first worker exit with CODE after its checks pass, and `network-package.mjs` runs it as the `worker exit code` scenario, which passes only when the probe fails with `Worker exited with code 7`; under the previous check that scenario fails. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GrHAFF8ZQVeLixLgm3YWBr
zapi prepublish only warns about a missing artifact, nothing tied a published addon to the one that passed qualification, and CI qualified again on every push to main that Release already qualifies. - `release_artifacts.mjs record` writes `build.json` beside the addons: each advertised target's hash, checked against its ELF or Mach-O machine, and the build toolchain and image. - `release_artifacts.mjs verify`, run after `zapi prepublish` in both publishing jobs and in the platform workflow's new `packages` job, fails unless the main package depends on exactly the advertised platform packages, each is complete and holds its recorded addon, and that addon passed the lifecycle qualification with a consumer lockfile. - The load probe prints the addon hash and the runtime, and `network-package.mjs` writes them with the scenario results to `qualification.json`; each qualify job uploads it with the consumer's `pnpm-lock.yaml`. - `check_network_package.mjs` keeps a host run's consumer with `--consumer` and only installs one with `--prepare`. - CI qualifies pull requests only; pushes to main qualify in Release, which publishes what that run qualified. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GrHAFF8ZQVeLixLgm3YWBr
…builds The build job's install ran the package's prepare script, whose Cargo build of quiche runs outside the --locked wrapper before the wrapped build, and a failed build uploaded nothing. - The build job installs with `pnpm install --frozen-lockfile --ignore-scripts`, as the qualify, packages and publishing jobs already do; the wrapped `zapi build-artifacts` stays the only build. - The build upload also runs on failure, keeping the addons of the targets that finished, as the qualification upload does. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GrHAFF8ZQVeLixLgm3YWBr
The publish guard accepted any nonempty passing results with the lifecycle flag, so a load-only record or one unrelated check qualified an addon. - `network-package-scenarios.mjs` lists the packaged consumer's scenarios: `network-package.mjs` runs them, and `release_artifacts.mjs verify` requires each target's record to name every lifecycle scenario exactly once, all passing. - The test release records every lifecycle scenario, and new cases refuse a load-only record, a missing, repeated or substituted scenario, and a failing one. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GrHAFF8ZQVeLixLgm3YWBr
…ckout `record` stored whatever `git rev-parse HEAD` printed in the working directory, stderr included, even when git failed. - `record` runs git in the package directory, requires it to be the top of its own checkout and HEAD to verify as a commit, and fails otherwise. - The release fixture is its own git repository; a new test records its HEAD and refuses a nested package, an unborn HEAD and a directory outside any checkout. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GrHAFF8ZQVeLixLgm3YWBr
Packaging and release publishing kept separate build records: `check_network_package.mjs` wrote its own for `lodestar_package.mjs pack`, while publishing trusted `build.json`. - `build.json` also records the release build's command and flags (`RELEASE_BUILD`) and the build host's architecture, so it keeps target hashes, build flags, source commit and toolchain in one record. - `packageBuildRecord` projects one target's packing record from it, which `pack` validates as before. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GrHAFF8ZQVeLixLgm3YWBr
The packages job verified the prepared packages without adding the notices, and the provenance record still named the publishing workflows as the build, so its check failed once the release built in the platforms workflow. - The packages job runs `lodestar_package.mjs notices` between `zapi prepublish` and verification, as both publishing workflows do. - `release_artifacts.mjs verify` refuses a platform package that does not list and carry the main package's legal files byte for byte, so verification fails if the notices step is skipped. - The provenance record lists each release workflow's steps, and the check requires them in that order: build, record and qualify, then prepublish, notices, verification and publishing. - The check also requires the record to list `RELEASE_BUILD.command`, the build `build.json` records. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GrHAFF8ZQVeLixLgm3YWBr
6.1a and 6.1b each packed and probed packages their own way: the platform qualification skipped the archive verification and the legal files, and the host probe ran a second lifecycle harness beside the shared fixtures. - `check_network_package.mjs` packs a target's release packages with `lodestar_package.mjs pack` from the `build.json` projection (prepublish, legal files, archive verification), or takes a `--manifest` packed before in either layout; it keeps cross-target `--prepare` and retained `--consumer` directories. - The consumer overrides every other platform package away, resolves its lockfile, or replays one with `--lockfile`, installs frozen from it, and requires the installed files to equal the archives'. It retains the manifest and build record with the lockfile and `qualification.json`. - The load probe resolves and imports every package export from the consumer through `lodestar_package_probe.mjs`, requires the loaded addon alone, and handles the embedded layout, so export resolution runs on every target. - `lodestar_package_probe.mjs` drops its own lifecycle; the fixture suite is the only lifecycle implementation, and the Lodestar host verification keeps export resolution and addon identity. - Packing the platform layout needs no `zig-out/lib`, which the qualification and publishing jobs do not build, and inspects native exports only for this host's target. - The qualification evidence also keeps the manifest and build record. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GrHAFF8ZQVeLixLgm3YWBr
The platform workflow now verifies archives on its Darwin runners too, whose `tar` is bsdtar, and the listing parser accepted only GNU tar's `tar -tv` columns. - `parseVerboseEntry` reads the size from either layout: GNU tar's `MODE OWNER/GROUP SIZE ...` and bsdtar's `MODE LINKS OWNER GROUP SIZE ...`. - The legal-files test inspects the same archive through bsdtar, where installed, and expects the same result. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GrHAFF8ZQVeLixLgm3YWBr
…equired With `--require-fetched`, a notice whose recorded license source could not be read was only listed apart, so a green check did not guarantee every located text was compared. - `checkProvenance` reports `NoticeSourceUnavailable` for such a notice when sources are required; without the flag it still lists it and passes. - The fetched-packages test points one crate's notice at a missing file and expects exactly that error under `requireFetched`. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GrHAFF8ZQVeLixLgm3YWBr
The provenance record still called the Rust toolchain unpinned and repeated its version in reviewed annotations, although `rust-toolchain.toml` now pins 1.91.1 for every Cargo build. - The record states the pin once, `rust.toolchain`, and the check requires it to equal the `rust-toolchain.toml` channel. - When notices are compared against sources, the check requires the selected `rustc` to be the pinned one, so the Rust library texts compared are that toolchain's, and every `rust-std/<crate>@<version>` notice to name a crate of its standard library lockfile. - The rust-std annotation drops "the toolchain is not pinned". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GrHAFF8ZQVeLixLgm3YWBr
The bindings test job installed with lifecycle scripts, whose `prepare` built the addon with a Cargo that could rewrite quiche's lockfile, and then built it again outside the `--locked` wrapper. - The job installs the pinned Rust toolchain, installs dependencies with `--frozen-lockfile --ignore-scripts`, and builds the addon through `scripts/zig-cross.sh`, which adds `--locked` to Cargo builds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GrHAFF8ZQVeLixLgm3YWBr
…ackaged probe The packaged load probe records the gossip SHA-256 backend the installed addon selected and fails when it differs from `LODESTAR_Z_EXPECT_GOSSIP_SHA256`, so each target's qualification can require its backend. - `network-package-probe.mjs` reads `NativeNetworkRuntime.gossipSha256Backend()`, calls `assertExpectedGossipSha256` and prints the backend, which `qualification.json` keeps - `check_network_package.mjs` copies `check_network_addon.mjs` into the consumer for the probe to import Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GrHAFF8ZQVeLixLgm3YWBr
Platform qualification never set `LODESTAR_Z_EXPECT_GOSSIP_SHA256`, so the probe skipped its backend check; each target now names the backend its addon must select from the runner's CPU rather than the detector under test. - x86_64 Linux expects `x86_sha_avx2` when `/proc/cpuinfo` has `sha_ni` and `avx2`, else `zig_std`, and the container run receives it with `-e` - x86_64 macOS reads `SHA` and `AVX2` from the `machdep.cpu` sysctls; a failed sysctl fails the step - aarch64 Linux and macOS expect `zig_std`, since the release build does not dispatch on ARM Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GrHAFF8ZQVeLixLgm3YWBr
An inbound request past the per-identity starts limiter is now admitted and waits in `.ready` until its start is due, instead of having its stream reset before the multistream echo leaves.
- `Limiter.startAt` returns when the identity's next start is due, a retry hint in the style of `eligibleAt` that reserves nothing
- `Server.accept` admits such a request with `start_pending` and the hint; `readRequest` keeps the hint across decoding and keys the wait on the deadline heap
- `Server.promote` rechecks and charges the start once, before serving; a request arriving behind one still owed its start waits too, and the longest `.ready` waiter of the class on the connection takes each refill
- protocol concurrency, peer capacity and identity capacity still reset, and waiters keep the 60 s quota deadline
- the `request_starts` refusal reason is removed; `lodestar_native_reqresp_inbound_occupied{phase="waiting_start"}` counts waiters and `request_start_wait` logs each wait
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GrHAFF8ZQVeLixLgm3YWBr
A stock js-libp2p requester sends 40 sequential blocks-by-root requests to the native runtime, and every one negotiates and completes. - the 37th and later requests wait for their start, and the test counts their `request_start_wait` records - the count is asserted only when the first 36 requests take under 9 s, since a slower host never exhausts the quota Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GrHAFF8ZQVeLixLgm3YWBr
…n identity row A request arriving behind one still owed its start is refused for identity capacity when its identity has no limiter row and none is free, as an arrival that charges its start already is. - `Limiter.tracks` checks for a row without charging, and `Server.accept` calls it before queueing the request uncharged - already admitted waiters keep waiting, for a free row if theirs was reclaimed - the regression test reclaims the waiting identity's row for another identity, then expects the next request refused and the waiter served once a row frees - `startQueued` states its bound only while the identity keeps a row and no other connection spends its starts Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GrHAFF8ZQVeLixLgm3YWBr
…the stock test The stock start-quota test now sends requests until a `request_start_wait` record shows the quota exhausted, then four more, instead of inferring exhaustion from how long the first 36 took. - every request must still negotiate and complete, and no `request_admission_refused` record may appear - the loop stops at 400 requests, so a host that cannot outpace the 3.6 per second refill fails rather than passing without a wait Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GrHAFF8ZQVeLixLgm3YWBr
Own the bounded LevelDB API in src/leveldb and link the pinned ChainSafe C artifact. Expose asynchronous reads, atomic batches, projected snapshot cursors, maintenance operations and lifecycle-safe NAPI bindings for Lodestar workloads. Include declarations, workload and ownership regressions, CI checks, documentation and dependency provenance. ReleaseSafe core and all 31 LevelDB binding tests pass; the full binding suite retains the known missing ERA fixture failures.
Remove branch-specific line-count tooling, dependency-graph rewrites and copied-release/link-switch orchestration. Preserve package archives, exports, provenance, legal checks and the committed LevelDB integration. Validation: 40 focused package checks passed on the unchanged final worktree.
Keep alternate-address-family candidates eligible and avoid consuming attempts before admission. Include the local ENR when an unknown peer advertises sequence zero. Validation: full discovery module passed 204 tests on these source bytes.
Validate native Ping and Metadata sequence values against the wire u64 contract. Keep host-facing range numbers bounded and use a range request to test neutral host-integer refusal. Validation: final full mainnet network module passed 1247 tests; targeted attribution and native-u64 cases passed under both presets.
Report stopped transport capacity as available so descriptor snapshots do not manufacture live occupancy after shutdown. Validation: metrics regressions passed under both presets and the final mainnet network module passed 1247 tests.
Reject configured scheduled subscription demand above topic capacity before starting the runtime. Include prepare-before-removal overlap and configured protocol rules; retain atomic runtime refusal checks. This is only a scheduled-demand guard. Capacity consumed by retained score/backoff/validation/outbound state remains a separate open issue. Validation: chain regressions passed under both presets, final mainnet network module passed 1247 tests, and fresh-addon network/LevelDB suites passed 221 tests.
Keep the native aggregate prefilter conservative for Deneb and for current/previous epoch timing with maximum clock-disparity grace. Add the reachable sibling regression suite. Validation: metadata and lifecycle selections passed under both presets; final mainnet network module passed 1247 tests.
The ssz_wire_types extractor writes one default-value encoding per selector beside the spec vectors; both are regenerated by zig build extract-corpus.
overnight.sh is a local launcher around fuzz-loop.sh, and the minimized network corpora are campaign output that --cmin regenerates from the queues, so both stay out of the tree. network_wire keeps its six hand-written seeds under corpus/network_wire-initial like every other network target.
Every ssz_wire_types seed is generated, so a fresh checkout has no corpus directory: the extractor now creates it, and the target's run step reads the initial directory rather than a cmin one that never exists.
quiche 0.30 answers a whole send call with Done when the stream at the head of its flushable queue emits no bytes: it rotates that stream to the back and breaks out, although the next call serves the next stream's data. The engine took the first Done as a drained burst and removed the connection from the dirty list, so the invariant that a clean slot has no output failed and the data would have waited for the next timer. sendOne now keeps calling while quiche answers Done, at most once per stream of the connection. Found by the quic_session fuzzer within an hour of adding datagram loss and the instrumented quiche: 136 crash inputs, all this invariant on the server's snapshot connection after a pump. The minimized input is a tracked seed; a unit test would need quiche's internal queue state.
quiche-zig b30cd14 carries the upstream hunk from cloudflare/quiche b60aff932 as a patch on 0.30.0: a stream whose pending retransmission a late ACK dropped stays in the flushable queue, and send returned Done with other streams' data still queued. With the library fixed, sendOne goes back to a single quiche call per datagram; the fuzz seed from 5015ccc stays as the regression.
Add Gloas gossip, payload-envelope req/resp, bounded dependency metadata, and fork-aware limits. Record the Lodestar network history audit and native mitigations.
Replace the overflowing fatal-identity list with generation-aware source cutoffs. Preserve unrelated queued messages, active host tasks, and admissions after the report. Add regressions for full source capacity, repeated reports, source reuse, delayed dependency checks, and gossipsub ingress. Verified all 1541 network tests, 32 minimal-preset checks, and 83 binding tests with the ReleaseSafe addon.
Centralize duplicate lookup across receipt, IHAVE recovery and publication. Record completed verdicts in the seen cache, and keep local cancellation distinct from a host ignore verdict. Release cancelled attribution and peer pins without blocking later copies. Preserve generation checks and host execution accounting across readmission, with regressions and stateful fuzz invariants. Validation: all 1544 network tests, 46 minimal-preset checks, 844 binding tests with the ReleaseSafe addon, three fuzz smokes, lint, tidy and tracked-source formatting.
Snapshot harness network_discv5_session drives a discv5 Engine against three real peer engines over a bounded in-process link: PING, FINDNODE and TALKREQ in both directions, standard and input-chosen responses, reordered, duplicated, corrupted, dropped and replayed datagrams including from spoofed sources, floods, expiry and deadline jumps, call cancel and fail, routing updates and the host's read-only queries. The node under test holds two sessions so a third handshake evicts one. Every step checks that the held call handles match the call table and that sessions, challenges and the routing table stay within capacity; teardown cancels every call and asserts empty tables. network-targets.sh now accepts digits in target names.
The corrupt operation flipped a datagram byte with 1 + byte, which overflows a u8 when the input byte is 255. Four AFL crashes within two minutes were this harness panic. Flip with byte | 1 instead, which is still never zero.
Share unchanged request allowances across IPv4 /24 and IPv6 /48 groups while retaining live and disconnected usage in one table. Derive table capacity from connection bounds and a global request-start quota so retained usage cannot refuse new connections or require debt eviction. Preserve the admission group across QUIC path changes, retain peer identity for serving concurrency, and cover identity rotation, churn, delayed charges, and malformed requests without a paid start.
Share bounded identity pins across validation sources, duplicates, and recovery promises. Preserve live peer capacity and delayed score ownership, cap pinned identities per address group, and handle local retention pressure explicitly. Validated with 1,552 network tests, 415 minimal-preset tests, ReleaseSafe bindings, and four fuzz smoke runs. The binding suite had one lifecycle timeout; all 34 tests in that file passed on rerun.
Charge application executions to the connection admission group through request and host retirement. Serve control replies from their reserved request buffers without an execution pool. Cover blocked control writes, copied responses, maximum error replies, slot reuse, and nonzero quota charges. Remove ineffective control serving settings from fixtures.
Give each authenticated peer its own control request and start allowances, so colocated peers can exchange Status, Ping, Metadata and Goodbye without consuming one another's maintenance quota. Keep application quotas and serving capacity keyed by address group. Retain usage across overlapping connections and reconnects of the same identity. Preserve global limits and the admission-derived capacity bound for retained history. Validate control accounting at protocol assembly and report peer quota refusals separately from group refusals. Cover colocated IPv4 and IPv6 peers, reconnects across addresses, stale connection retirement, identity churn, and the full control peer workload.
Share application quotas and serving slots per IPv4 address or IPv6 /64 so neighboring sources no longer share a wider-prefix allowance. Centralize address normalization and prefix keys while preserving captured account ownership, retained debt, and host-held serving leases.
Limit pinned gossip identities per IPv4 address or IPv6 /64 so neighboring sources no longer share a wider-prefix allowance. Preserve peer-ID attribution, the global retention budget, and source ownership until the final reference releases.
An input could send and receive NODES answers with 16 records each and force a cold handshake on every call, so a few hundred operations cost tens of seconds of secp256k1 verification and the campaign dropped to a few executions per second with 92 inputs over the 5 s timeout. Each input now ends after 128 verification equivalents; the slowest of those inputs takes 1.6 s.
Merge origin/main at 268208d into integration/all. Preserve network blob-schedule selection and request-type coverage alongside upstream Heze consensus support. Combine the fork-configuration regressions and regenerate the dependency lockfile.
Inputs that black-hole one direction and jump the clock make the pair deliver millions of datagrams in a thousand steps, about 7 s each under coverage instrumentation and above the campaign timeout, while the corpus median is 21 datagrams and the maximum 138. The pair now counts delivered datagrams and the harness ends an input at 16,384. The three recorded inputs run in 0.15 s.
Distinguish local admission pressure from peer request limits and return a runtime-scoped token for the exhausted resource. Track releases independently so retiring a refused request cannot trigger retries against unchanged native capacity. Add bounded, cancellable waits and coalesced host notifications, with coverage for early releases, shutdown, callback failures and request-table reuse.
Preserve native fuzz coverage and clock-shim replay while adopting upstream ReleaseSafe builds and corpus minimization. Update the gossip harness to the fork-aware metadata interface.
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.
Motivation
Ethereum P2P traffic is both bursty and adversarial. A peer can authenticate correctly and still send incomplete frames, advertise work that never arrives, withhold stream progress, or create more validation and serving work than the node can finish. Healthy slot-boundary bursts also exhaust queues. The node needs explicit rules for which work it admits, which work it retains, and when it recovers capacity.
This branch puts discovery, transport, protocol state, and gossip scheduling under one native owner. The goal is to make the cost and lifetime of remote work visible across the whole path: receipt, decoding, validation, forwarding, response production, and retirement. An item limit alone misses large payloads; a byte limit alone misses many small jobs; a timeout alone does not stop a database read or validation task that already started. Those distinctions drive the design.
The workload also includes substantial block and data-column fanout. Shared payload storage, explicit writable events, and batched UDP I/O let the implementation serve that workload while keeping pressure decisions in the protocol owners. The accompanying Lodestar integration, ChainSafe/lodestar#10375, supplies consensus validation, chain state, serving sources, and operating policy.
Description
Adds a native Ethereum beacon networking stack and Node binding, with bounded admission and explicit ownership from sockets through host delivery.
Ownership and execution
udp,discv5, andquicNetworkCoreand its protocol ownersNetworkCore.advanceperforms one bounded turn from supplied readiness and time. The driver waits on socket/host readiness or the earliest scheduled work, then advances the core with a fresh clock. A blocked stream waits for writable readiness; retained events and unfinished bounded work keep the owner runnable. Monotonic deadlines are distinct from Unix time and consensus slots. This gives socket-driven production operation and deterministic tests the same protocol entry points.The Node API exposes
createNativeNetwork(config, host), promise-returning operations, and response iterators. Its internalexchangeapplies host actions and delivers bounded batches. Capacity notifications can coalesce and cause a fresh capacity read; they grant no reservation. Status and subscription intent can coalesce to the latest desired state, while operation completions and close obligations retain their own lifetimes. The public binding contract documents these distinctions.Capacity and behavior under pressure
configurationandReservations: derive table and buffer requirements at startup and reject configurations that do not fit the configured budgets. Bound retained entries, payload bytes, and work per turn separately. Accounting covers allocations made through the owning allocator; its limits are not a whole-process RSS guarantee.delivery, gossip admission, and serving pools: combine global limits with per-peer or per-source allowances and dedicated capacity for control work. Forwarding descriptors have a protected per-peer share, with bounded borrowing from the remainder. Bulk traffic must not consume the capacity needed to complete control and lifecycle work. These rules limit monopolization; they do not establish that distinct peer identities are independent operators.active_send: distinguish a queued forward from a frame already started on a reliable stream. Small active frames own a per-session copy; large frames retain shared payload backing under a separate per-kind allowance. History expiry can discard queued work without invalidating started frames. Large recipients share the payload's first-send deadline, so adding recipients does not extend its lifetime. An active-send deadline violation closes the connection. Complete-frame ordering also means control traffic can wait behind that frame for its bounded lifetime.The beacon integration supplies the policy values, including message-kind limits and receive/send deadlines. They remain configurable at initialization. The adversarial assumptions and the limits of native/dependency accounting are documented in THREAT_MODEL.md.
Lifecycle and failure handling
One network runtime is live per process.
stopDeliverystops new host work while completion processing continues.closestops native work, joins the owner, and resolves outstanding operation obligations; already-started host tasks can finish later. Explicit close and Node environment teardown both have cleanup paths.Operational failures belong to the affected request, publication, or connection. Infrastructure failures terminate the runtime with their original cause. Once an exchange has committed ownership transitions, failure to construct or publish its result while JavaScript is still runnable is a fatal bridge-contract failure. Silently losing that result would strand obligations on both sides of the binding.
Other scope in this draft
leveldb: adds bounded reads and result materialization, copied asynchronous inputs, and explicit cursor ownership through the native addon. Each handle allows two concurrent point/multi-get reads alongside serialized writes, cursors, and maintenance. Callers await writes before dependent reads. These are application admission/result bounds, not bounds on LevelDB's internal cache, compaction, or total memory. The companion controller migration applies to both network backends; the storage contract describes the independent adoption decision.quiche-zig, including STOP_SENDING cause propagation and the C transport-parameter layout fix. Linux UDP uses boundedrecvmmsg/sendmmsgbatches and GSO with fallback. The branch also includes the ere archive compatibility work needed to link/load the combined addon.Validation
Recent local results for the current Zig revision:
The binding run used the ReleaseSafe mainnet addon for
x86_64-linux-gnu.2.35, CPUznver1. All ten network fuzz targets built in ReleaseSafe, replayed 120 corpus inputs, and passed three-second AFL smoke runs. The new transport-parameter handshake regression failed against the old dependency and passes with the patch. Repository formatting, tidy, and lint checks pass; formatting excludes downloaded dependency caches.The preceding branch review also ran UDP, LevelDB, config/runtime, minimal-preset support, network tools, independent JS-libp2p interop, and real-collector Xray integration checks. These checks are distinct from full platform qualification and sustained adversarial fuzzing.
Before merge
The native beacon path currently supports through Fulu. A future Gloas schedule is allowed, but activating Gloas or requesting unsupported native topics/serving is rejected.
AI Assistance Disclosure
Implementation, tests, and this description were primarily authored with OpenAI Codex, with architecture and policy decisions developed through iterative maintainer direction. This is a draft for human review.