Skip to content

Rebase #741 onto neo/edge with review fixes#839

Open
Lucifer0x17 wants to merge 33 commits into
neo/edgefrom
feat/rebased-router-node
Open

Rebase #741 onto neo/edge with review fixes#839
Lucifer0x17 wants to merge 33 commits into
neo/edgefrom
feat/rebased-router-node

Conversation

@Lucifer0x17
Copy link
Copy Markdown
Collaborator

Replaces #741.

Clean rebase of feat/permissionless-id-fetch onto current neo/edge. The original branch had drifted enough that its diff showed 32 files changed when only 8 are actual feature work. This strips out the staleness.

Includes everything from #741 plus

  • permissionless TX serving in dev_arweave
  • chance@1.0 gate device (replaces monitor-sampler)
  • hook based http-client monitor
  • dev_hook nested name lookup via deep_get

Review fixes on top

  • hb_http_client: cleaned up leftover record_duration after a botched rebase merge, fixed the prometheus metric name and wrapper, added action label to the hook body
  • dev_router_perf:register: restored the admission control gate (trusted-peer or is-admissible) that the Lua original had
  • dev_arweave:get_tx: handle {failure, _} from dev_hook:on so hook misconfig does not crash
  • dev_chance: validate rate input before parsing
  • TODO on update_node_performance flagging cross-route http_reference collision

No new tests yet. router-perf is not in prod so we land the fixes first and add tests separately.

Arweave v2 TXIDs are SHA-256(signature), which differs from the unsigned
  content hash used by HyperBEAM messages. Add a fallback in is_tx_admissible
  that checks the commitment-based ID via hb_message:id(CommittedMsg, all, Opts)
  when the unsigned content hash doesn't match.
…ed routing

Introduces dev_router_perf.erl, an Erlang execution device for process@1.0 that replaces dynamic-router.lua for Arweave gateway routing. Fixes gaps in the Lua version: supports match/with route format.

Key functionality:
   - register: adds nodes to routes with http_reference for the feedback loop
   - duration: updates node performance via exponential weighted average (EMA)
   - recalculate: recomputes weights using decay-based percentile scoring
   - Supports both match/with and prefix

Registers router-perf@1.0 in hb_opts preloaded_devices.

Comments added in the file by: Claude Opus 4.6 <noreply@anthropic.com>
Add `is_admissible_hook_routed_test_` that validates the full perf-router feedback loop: gateway registration, TX fetch through routed stack, async monitor duration posts, and performance score updates via EMA.
Lightweight device that gates HTTP monitor invocations via 1-in-N probabilistic sampling. Reads `sample-rate` from the request message and rolls `rand:uniform(Rate) =:= 1`. If absent, all requests pass.
- Gate monitor invocations via monitor-sampler@1.0 when `sample-rate` is set in the http_monitor config
- When `is-wasm-process` is set, strip the reserved `path` key from the monitor body and move it to `monitor-type`.

opts example
---
``` json
  "http_monitor": {
    "device": "relay@1.0",
    "method": "POST",
    "peer": "http://localhost:9000",
    "path": "call",
    "relay-path": "/<process-id>/push",
    "commit-request": true,
    "sample-rate": 10,
    "is-wasm-process": true
  }
```
Change find/3 to use hb_util:deep_get instead of maps:get so that slash-separated hook names like <<"http-client/response">> resolve
to nested keys in the on config map
Add dev_chance.erl as a composable 1-in-N probabilistic gate using the 4-arity default handler pattern.
- Replace ~60 lines of custom monitor invocation (maybe_invoke_monitor, should_forward, do_invoke_monitor, sanitize_body) with a single `dev_hook:on(<<"http-client/response">>, ...)` call.

- Reorder relay-path priority in dev_relay:call so that the explicit relay-path key is checked before the generic path key, fixing a conflict where the handler's dispatch path shadowed the relay URL.
The multirequest-admissible check was in the generic request/5 function which also handles /raw paths that don't emit the user's message as-is. Moved the admissibility + tx-admissible hook logic into get_tx where it belongs.
- Remove duplicate record_duration/2 and dead maybe_invoke_monitor/2 left
  by a botched merge during the rebase onto neo/edge.
- Switch the surviving record_duration/2 to use hb_prometheus:observe/3
  with the declared metric name http_client_duration_seconds (was calling
  prometheus_histogram:observe directly with an undeclared name).
- Add <<"action">> => <<"duration">> to the hook body so dev_router_perf
  can dispatch performance updates from the http-client/response hook.
Restores the admission control gate that the Lua dynamic-router had but
the Erlang port dropped. Without this anyone could POST a registration
and inject themselves into a route.

- Add trusted-peer check: if any signer of the request matches the
  configured trusted-peer wallet, registration is accepted unconditionally.
- Otherwise resolve the configured is-admissible device against the body.
  If absent, registration is open (matches prior behaviour).
- Document trusted-peer and is-admissible at the module level.
- Drop the path-fallback dispatch and TODO from compute/3 now that
  hb_http_client always sets <<"action">> on the hook body.
dev_hook:on/3 can return {failure, Reason} when a handler returns an
unexpected result. Without this clause get_tx crashes with case_clause
on hook misconfiguration. Treat it the same as {error, Reason}.
binary_to_integer/1 throws badarg on a non-integer path. Wrap in
try/catch and reject non-positive rates explicitly so callers get a
proper error tuple instead of a crash.
@nikooo777 nikooo777 force-pushed the feat/rebased-router-node branch from c748996 to 8b5c56d Compare April 13, 2026 16:58
response_to_map/1 only matched {ok, _, _, _}. Transport errors from
hackney/gun crashed with function_clause before record_duration could
fire the hook. Add a catch-all that returns an empty map so the hook
chain still runs (and the adapter rejects the empty response as
not admissible).
Comment thread src/dev_arweave.erl
%%% Helper functions
-export([get_chunk/2, bundle_header/2, bundle_header/3]).
-include("include/hb.hrl").
-include("include/hb_arweave_nodes.hrl").
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why?

Comment thread src/dev_arweave.erl
<<"path">> => <<"is-tx-admissible">>,
<<"tx">> => TXID
},
case is_tx_admissible(Admissible, Msg, TxOpts) of
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we using a newly constructed message with a path as a base message?

Comment thread src/dev_arweave.erl
},
case is_tx_admissible(Admissible, Msg, TxOpts) of
true ->
case dev_hook:on(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you want to ignore the result. It shouldn't affect the user getting their reply, unless I misunderstood?

Also, if we have a function called is_tx_admissible, why is our tx-admissible hook outside of it?

Comment thread src/dev_arweave.erl
maybe
{ok, TXID} ?= hb_maps:find(<<"tx">>, Base, Opts),
CommIDs = maps:keys(maps:get(<<"commitments">>, Request, #{})),
true ?=
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is unnecessary

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants