feat: js-libp2p compatibility — zero-copy JSI data path + Node net.So…#232
Open
tswindell wants to merge 1 commit into
Open
feat: js-libp2p compatibility — zero-copy JSI data path + Node net.So…#232tswindell wants to merge 1 commit into
tswindell wants to merge 1 commit into
Conversation
…cket half-open parity (Rapsssito#209) Fixes Rapsssito#209 (Task 2: RN clients failing to connect to libp2p bootstrap/relay/peer nodes with `UnexpectedEOFError`). Task 1 (Rapsssito#183, `net.createServer(options, cb)`) is already resolved as of v6.4.1 — no code change needed. Root-caused four independent defects that made js-libp2p v3 unusable on Android RN 0.83 (New Arch / bridgeless / Hermes), each device-proven on a physical device (Nokia 8.3 5G, arm64-v8a) against the public Amino DHT swarm: 1. connect() option-graph OOM. @libp2p/tcp spreads its entire cyclic dial-options object (signal/upgrader/components) into the net.connect() arg; RN's jsi::dynamicFromValue recursively explodes it into millions of folly::dynamic nodes → Scudo OOM in seconds. Fixed with an explicit scalar allow-list (the native side only ever reads those keys). 2. Per-chunk legacy-bridge data path OOM. Any per-chunk crossing of the RCTDeviceEventEmitter / invokeJavaMethod bridge backlogs unbounded folly::dynamic under libp2p volume. Replaced inbound + outbound byte movement AND the readable/written signals with a zero-copy JSI HostObject (cpp/TcpDataBridge) driven by the C++ CallInvoker — bytes and signals never transit folly::dynamic. 3. JNI registration lifetime. In the New-Arch single-merged-.so model the data-bridge JNI_OnLoad ran pre-bundle under the bootstrap classloader; native methods failed to bind. Fixed with an implicitly-bound, Java-driven install entry + a link-time keep anchor (defeats --gc-sections stripping of the runtime-only TU). 4. JSI install guard on a HostObject. The "callback installed" guard was stashed as a property on the JSI HostObject; Hermes rejects arbitrary-property writes on a HostObject with a default setter (TypeError), which was swallowed → bridge null → every noise write failed → every dial silently abandoned (conns=0). Moved the guards to module-level state. Also implements the Node net.Socket half-open / teardown contract @libp2p/tcp depends on (and which the bounty's connect failure surfaces): - connect() now reads `options.allowHalfOpen` (kept off the native bridge — JS lifecycle flag only). - Adds socket.destroySoon() (libp2p sendClose calls it on every graceful close — its absence threw `is not a function` on every connection teardown). - Adds socket.resetAndDestroy() (libp2p sendReset). - 'close' now emits a boolean `hadError` per the Node spec (was the raw error object, working only by truthiness). - destroy(error) accepts an optional error per the Node signature. iOS JSI parity is a scoped follow-up: the shared JS changes are iOS-correct, but the C++ data path is not yet wired into the iOS pod. The legacy iOS path is unaffected; only the new zero-copy JSI path is Android-only for now. Adds __tests__/halfOpen209.test.js (6 tests). Full suite green (16/16). No version bump (release is the maintainer's call). Server.js / index.js / TLS*.js intentionally untouched.
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.
…cket half-open parity (#209)
Fixes #209 (Task 2: RN clients failing to connect to libp2p bootstrap/relay/peer nodes with
UnexpectedEOFError). Task 1 (#183,net.createServer(options, cb)) is already resolved as of v6.4.1 — no code change needed.Root-caused four independent defects that made js-libp2p v3 unusable on Android RN 0.83 (New Arch / bridgeless / Hermes), each device-proven on a physical device (Nokia 8.3 5G, arm64-v8a) against the public Amino DHT swarm:
connect() option-graph OOM. @libp2p/tcp spreads its entire cyclic dial-options object (signal/upgrader/components) into the net.connect() arg; RN's jsi::dynamicFromValue recursively explodes it into millions of folly::dynamic nodes → Scudo OOM in seconds. Fixed with an explicit scalar allow-list (the native side only ever reads those keys).
Per-chunk legacy-bridge data path OOM. Any per-chunk crossing of the RCTDeviceEventEmitter / invokeJavaMethod bridge backlogs unbounded folly::dynamic under libp2p volume. Replaced inbound + outbound byte movement AND the readable/written signals with a zero-copy JSI HostObject (cpp/TcpDataBridge) driven by the C++ CallInvoker — bytes and signals never transit folly::dynamic.
JNI registration lifetime. In the New-Arch single-merged-.so model the data-bridge JNI_OnLoad ran pre-bundle under the bootstrap classloader; native methods failed to bind. Fixed with an implicitly-bound, Java-driven install entry + a link-time keep anchor (defeats --gc-sections stripping of the runtime-only TU).
JSI install guard on a HostObject. The "callback installed" guard was stashed as a property on the JSI HostObject; Hermes rejects arbitrary-property writes on a HostObject with a default setter (TypeError), which was swallowed → bridge null → every noise write failed → every dial silently abandoned (conns=0). Moved the guards to module-level state.
Also implements the Node net.Socket half-open / teardown contract @libp2p/tcp depends on (and which the bounty's connect failure surfaces):
options.allowHalfOpen(kept off the native bridge — JS lifecycle flag only).is not a functionon every connection teardown).hadErrorper the Node spec (was the raw error object, working only by truthiness).iOS JSI parity is a scoped follow-up: the shared JS changes are iOS-correct, but the C++ data path is not yet wired into the iOS pod. The legacy iOS path is unaffected; only the new zero-copy JSI path is Android-only for now.
Adds tests/halfOpen209.test.js (6 tests). Full suite green (16/16). No version bump (release is the maintainer's call). Server.js / index.js / TLS*.js intentionally untouched.