polygon-gateway is a bounded Polygon PoS P2P relay. It connects trusted upstream Bor peers and
one explicitly configured local Bor target, receives eth block announcements, and sends complete
NewBlock payloads to the target.
Current implementation:
- Amoy and Polygon Mainnet chain IDs, genesis data, EIP-2124 fork IDs, and live-head status.
- Bor
0x7f StateSyncTxand receipt wire decoding, hashing, limits, and trie-root support. eth/68RLPx sessions with explicit source and target roles.- Bounded candidate-window selection with conflict freezing and source-quorum reorg resolution.
- Retried, session-aware delivery with target P2P observations or optional RPC confirmation.
- Header/body fetch from the announcing peer after a trusted TD anchor is available, with fallback to another connected source.
- Bounded exponential retries for transient header/body fetch failures.
- Header hash, block number, and transaction-root verification before fetched blocks are sent.
- A bounded, target-only proxy for header, body, node-data, and receipt requests to source peers.
- Bounded ingress/fetch queues, eight concurrent hash fetches, inflight deduplication, and TD cache.
- A 256-block full-body cache covering twice the configured maximum reorg depth.
- Periodic structured health logs and feature-gated hotpath profiling.
Bor 2.10.0 retains TD in its eth/69 Status packet, while upstream Reth 1.11.4 omits it. The
gateway therefore advertises only eth/68 until a Bor-specific eth/69 handshake codec is added.
cargo build --release -p polygon-gateway
cargo test -p polygon-protocol -p polygon-gateway-core -p polygon-gatewayThe target and every source are explicit enode URLs. Discovery is disabled so an unclassified peer cannot become a relay source or target.
./target/release/polygon-gateway \
--network mainnet \
--rpc-url https://YOUR_POLYGON_MAINNET_RPC \
--total-difficulty EXACT_TD_AT_ANCHOR \
--total-difficulty-block-hash 0xANCHOR_BLOCK_HASH \
--target-node 'enode://TARGET_PUBLIC_KEY@TARGET_IP:30303' \
--source-node 'enode://SOURCE_PUBLIC_KEY@SOURCE_IP:30303' \
--source-quorum 1 \
--listen-addr 0.0.0.0:30313Repeat --source-node for redundancy and set --source-quorum to the number of independent peers
required to resolve a same-height conflict. Mainnet is the default network. --total-difficulty
is mandatory because Bor HTTP RPC commonly omits it. With --total-difficulty-block-hash, the
gateway verifies that anchor and derives the current TD over at most 2,048 RPC blocks. Without an
anchor hash, the supplied TD must exactly match the RPC head sampled at startup. The gateway fails
closed instead of substituting block height when TD is unavailable. Use --network amoy for Amoy.
--receipt-probe-hash optionally verifies the live Polygon receipt decoder against a configured
source and logs both the receipt count and the number of 0x7f receipts.
The default --confirmation-mode p2p treats a matching block announcement from the target session
as evidence that the target knows the block. Use --confirmation-mode rpc only when --rpc-url
points at that exact target Bor. Reth's send API does not expose its bounded session-queue result,
so an enqueued log is a send attempt, not an import acknowledgement; unacknowledged candidates
remain pending and are retried after reconnect.
Successful data-plane logs contain:
P2P session established ... role="source"
P2P session established ... role="target"
enqueued full block for local Bor delivery ... number=... hash=... td=... attempt=...
target acknowledged relay candidate ... evidence=p2p number=... hash=...
relay health ... pending_delivery=... send_retries=... p2p_acknowledged=... fetch_errors=...
Health logs also report first_send_latency_us_avg and first_send_latency_us_max. These measure
validated-block storage to first target-session enqueue time; they do not claim remote import or
end-to-end acceleration without a target observation.
The default build keeps hotpath instrumentation disabled. Build timing, allocation, custom gauge, thread, and Tokio runtime profiling with:
make profileRun a monitored soak test by passing the upstream enodes as positional arguments:
TARGET_NODE='enode://TARGET_PUBLIC_KEY@127.0.0.1:30303' \
TOTAL_DIFFICULTY=EXACT_TD_OF_RPC_HEAD \
TOTAL_DIFFICULTY_BLOCK_HASH=0xANCHOR_BLOCK_HASH \
RPC_URL=https://YOUR_POLYGON_MAINNET_RPC \
DURATION_SECONDS=3600 \
scripts/soak-test.sh \
'enode://SOURCE_1_PUBLIC_KEY@SOURCE_1_IP:30303' \
'enode://SOURCE_2_PUBLIC_KEY@SOURCE_2_IP:30303'Each run writes gateway.log, process.csv, tokio-runtime.jsonl, hotpath.json, and
summary.txt below artifacts/soak-<timestamp>/. The relay queue is deliberately not wrapped with
hotpath::channel!: the hotpath proxy adds one slot and can change try_send backpressure
semantics. Queue depth and drops are recorded directly instead.
This is an early implementation of the reviewed design. Historical requests are proxied on demand; the gateway does not maintain a complete local history, so catch-up availability still depends on the configured sources. It does not implement Bor's eth/69 Status variant or claim consensus/finality validation.