Relayer authorization refresh
AstralBeam admits P2P relayers only when their Ethereum address is authorized
by both the EVM MultiSigERC20Locker and Casper EthClient.
Refresh ownership
The two bridge-direction workers each run a PeerDiscovery 60-second timer.
They share one registry only when the Ethereum endpoint and locker plus the
Casper endpoint, chain, and EthClient package all match. A configuration change
uses a separate registry and cannot inherit another network's cached work.
Discovery start/stop retains/releases one shared deadline scheduler. Authorization refresh runs independently of peer connection work, with its next deadline 60 seconds after each refresh start, including off-phase ingress recovery or force. Both chain operations must settle before the shared promise is released. A call longer than the interval delays the next attempt rather than overlapping it; provider availability still limits when fresh membership can be delivered.
Peer timers observe the scheduler's outcome; ingress may retry failed work early.
Force bypasses fresh cache and joins in-flight work. Invalidation makes older
successful work reject to waiting consumers, not merely skip cache publication.
It does not cancel RPCs; explicitly starting a new generation can overlap the
detached old work. Stopping the last discovery removes the scheduler. Casper
URL identity uses transport normalization (https://node equals
https://node/rpc).
Invalidation re-arms the retained authorization deadline even without a new ingress read. Stop/restart preserves that deadline (an overdue deadline runs on the next timer turn). A refresh starting at 60 seconds, invalidated at 65 and settling at 70, still retries at 120 with 17-second peer work; the old completion cannot release a newer generation's lock.
Casper snapshot and request budget
Every enumeration first selects one state root, then resolves the latest
enabled Casper 2 Package or legacy ContractPackage version at that root.
Count, threshold, every indexed address, and every authorization flag are read
from that same root and entity. Resolution is repeated on the next refresh so
contract upgrades are observed.
The Casper request budget is 4 + 2N: root, package, count, threshold, then an
address and flag for each of N relayers. At five relayers this is 14 requests
(previously 36). With two discovery instances started 30 seconds apart and a
10-second simulated refresh, measured starts over 180 seconds changed from
60/90/120/150/180 seconds to 60/120/180 seconds. The shared 60-second cadence
is unchanged; redundant offset work is removed. The repository runbook records
the root/package/dictionary request breakdown and regression fixtures.
Off-phase recovery/force fixtures start refreshes at 60/65/125/185 seconds with two actual discovery instances and real registry scheduling. Peer connection delays vary from 0 to 17 to 23 seconds without moving the authorization deadline. This fixes the freshness-only implementation's 85-second recovery gap.
Publication is all-or-nothing. A missing or malformed required value rejects
the candidate; a valid false authorization flag simply excludes that address.
The EVM/Casper threshold policy is unchanged, including the existing minority
warning and minimum-of-both effective threshold.
Reading failure logs
Casper lookup errors report kind, stage, method, elapsedMs, timeout, a
safe endpointLabel, and available httpStatus or rpcCode. The main stages
are state-root, package-resolution, relayer-count, threshold,
relayer-address, and authorization-flag.
Nested errors and bounded causes are retained in console and file output.
Secrets, credentials, headers, request payloads, URL userinfo and URL query
values are redacted. Only code -32003 together with recognized
ValueNotFound text is treated as a missing dictionary entry; other failures
remain failures, including processed-event checks.
Remote JSON-RPC message/data are omitted from direct errors and logs; canonical
labels and numeric codes remain. Timeouts while reading the HTTP body retain
kind=timeout; body transport failures are not mislabeled as malformed JSON.
A real loopback server with an unfinished JSON body exercised the production
10-second abort signal: timeout at 10002 ms, then 10004 ms on rerun, with
TimeoutError cause. This was
a local transport test, not a captured live-node fault.
At the direct RPC boundary, transport and body-parser exceptions are reduced
to an allowlisted name/code and a bounded chain of similarly reduced causes.
Raw exception messages, stacks, arbitrary properties and response snippets are
never retained, including in direct CLI output outside the logger. Unknown names
become Error; unknown codes are omitted. The outer typed error retains safe
message, stack, stage, method, timing and status. Generic non-RPC nested error
logging remains unchanged. Native URL-credential rejection and native
Response.json() syntax failures are covered through the CLI and both actual
console/file formatters.
Strict mapping reads preserve root/package error context. Legacy display/value helpers still log typed errors at debug level and return null; the structured exception guarantee is scoped to authorization and strict mapping reads.
Stale data behavior
A failed refresh never advances the successful snapshot timestamp. With the
default maxStaleMs=900000, fallback is allowed through exactly 900000 ms and
fails closed only above that age. Zero disables the ceiling. Cold-start failure
does not create authorized membership.
Once the ceiling is exceeded, an authenticated /message request receives HTTP
500 before its handler runs. Discovery has a different, intentionally retained
outbound behavior: it keeps the previous peer view, emits the snapshot age, and
retries at the next tick. Operators should not interpret the ceiling as a claim
that every outbound relay activity stops.