diff --git a/benchmarks/base-rpc.yml b/benchmarks/base-rpc.yml index 3b338eed..55f0510b 100644 --- a/benchmarks/base-rpc.yml +++ b/benchmarks/base-rpc.yml @@ -26,7 +26,7 @@ methodology: - "Call-result classification: `ok` (HTTP 200 + non-empty result), `http_err`, `jsonrpc_err` (HTTP 200 carrying an error body), `stale` (more than 20 blocks behind the cross-provider tip), `timeout`. Latency without reliability is a misleading ranking signal." - "Archive depth: every 5 minutes we issue `eth_getBalance` at (head − depth) for depths from Geth's default pruned cap up to 5M blocks, exposing which free endpoints actually serve historical state." - "This page is part of the per-chain RPC cluster derived from the cross-chain [rpc-capabilities](https://openchainbench.com/benchmarks/rpc-capabilities) benchmark; the identical harness, methodology and exclusion rules apply on every chain." - - "Chain scope: every query on this page is pinned to chain=\"base\". Provider coverage: 6 no-key endpoints (PublicNode, dRPC, Tenderly, Nodies, Merkle, Base). Merkle blocks datacenter IPs since 2026-07, so it is probed through a residential proxy; its latency includes proxy overhead and is flagged as such. Exclusions follow the cluster-wide rules documented on the parent benchmark." + - "Chain scope: every query on this page is pinned to chain=\"base\". Provider coverage: 5 no-key endpoints (PublicNode, dRPC, Tenderly, Nodies, Base). Merkle was delisted 2026-07: its gateway does not serve `eth_getBlockByNumber`, the anti-cache probe this cluster standardized on, so it cannot be measured on equal terms. Exclusions follow the cluster-wide rules documented on the parent benchmark." findings: - "{{best_name}} currently leads free Base RPC at {{best_p50}} (`eth_getBlockByNumber` p50, 24h) across 6 measured providers." @@ -36,7 +36,7 @@ faq: - q: "What is the fastest free Base RPC right now?" a: "{{best_name}} currently leads at {{best_p50}} (`eth_getBlockByNumber` p50 over the last 24h), measured against 6 no-key providers probed every 60 seconds from us-east, eu-west and Singapore. The leaderboard re-sorts continuously against fresh Prometheus samples, so the answer on this page is the answer right now, not a quarterly snapshot. Use the region tabs to see the leader from the origin closest to your deployment." - q: "Which Base RPCs work without an API key?" - a: "The 6 providers on this page: PublicNode, dRPC, Tenderly, Nodies, Merkle, Base. Merkle blocks datacenter IPs since July 2026, so its probes route through a residential proxy; the latency column includes that proxy overhead and the row is flagged accordingly. Every (provider, chain) pair was live-verified no-key before inclusion, and anything that key-gates, region-blocks or rate-limits below our 15-second cadence is excluded rather than listed with an asterisk." + a: "The 5 providers on this page: PublicNode, dRPC, Tenderly, Nodies, Base. Merkle was delisted in July 2026 because its gateway does not serve `eth_getBlockByNumber` (JSON-RPC -32601), the anti-cache probe used across the cluster, so its latency cannot be compared on equal terms. Every (provider, chain) pair was live-verified no-key before inclusion, and anything that key-gates, region-blocks or rate-limits below our 15-second cadence is excluded rather than listed with an asterisk." - q: "Does the fastest Base RPC change by region?" a: "Frequently. The headline number averages three probe origins (us-east, eu-west, Singapore), but per-region leaders regularly diverge, a gateway that wins from Virginia can lose from Singapore by multiples. The region tabs at the top of the page re-scope every number on the page to a single origin; pick the one closest to where your requests actually originate." - q: "How is Base RPC latency measured here?" @@ -155,29 +155,6 @@ providers: p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="nodies", chain="base", region="sgp"}) series: avg_over_time(rpc_latency_milliseconds{provider="nodies", chain="base", region="sgp"}[1h]) - - slug: merkle - name: Merkle - tag: Blocks datacenter IPs since 2026-07; probed via residential proxy, latency includes proxy overhead - formula: "50th percentile over 24h of round-trip latency (ms) for `eth_getBlockByNumber` every 60s from 3 regions via a residential proxy (Merkle blocks datacenter IPs since 2026-07); includes proxy overhead, not directly comparable." - queries: - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="base"}) - p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="merkle", chain="base"}) - p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="merkle", chain="base"}) - mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="merkle", chain="base"}) - success: sum(ocb:rpc_call:ok_rate_24h{provider="merkle", chain="base"}) / sum(ocb:rpc_call:rate_24h{provider="merkle", chain="base"}) - sample_size: sum(ocb:rpc_call:increase_24h{provider="merkle", chain="base"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="base"}[1h])) - regions: - - region: us-east - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="base", region="us-east"}) - series: avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="base", region="us-east"}[1h]) - - region: eu-west - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="base", region="eu-west"}) - series: avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="base", region="eu-west"}[1h]) - - region: ap-southeast - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="base", region="sgp"}) - series: avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="base", region="sgp"}[1h]) - - slug: base-official name: Base tag: Coinbase-operated, Base mainnet RPC diff --git a/benchmarks/bnb-rpc.yml b/benchmarks/bnb-rpc.yml index 68a5cad6..d85ecd35 100644 --- a/benchmarks/bnb-rpc.yml +++ b/benchmarks/bnb-rpc.yml @@ -26,7 +26,7 @@ methodology: - "Call-result classification: `ok` (HTTP 200 + non-empty result), `http_err`, `jsonrpc_err` (HTTP 200 carrying an error body), `stale` (more than 20 blocks behind the cross-provider tip), `timeout`. Latency without reliability is a misleading ranking signal." - "Archive depth: every 5 minutes we issue `eth_getBalance` at (head − depth) for depths from Geth's default pruned cap up to 5M blocks, exposing which free endpoints actually serve historical state." - "This page is part of the per-chain RPC cluster derived from the cross-chain [rpc-capabilities](https://openchainbench.com/benchmarks/rpc-capabilities) benchmark; the identical harness, methodology and exclusion rules apply on every chain." - - "Chain scope: every query on this page is pinned to chain=\"bnb\". Provider coverage: 5 no-key endpoints (PublicNode, dRPC, Nodies, Merkle, Binance). Merkle blocks datacenter IPs since 2026-07, so it is probed through a residential proxy; its latency includes proxy overhead and is flagged as such. Exclusions follow the cluster-wide rules documented on the parent benchmark." + - "Chain scope: every query on this page is pinned to chain=\"bnb\". Provider coverage: 4 no-key endpoints (PublicNode, dRPC, Nodies, Binance). Merkle was delisted 2026-07: its gateway does not serve `eth_getBlockByNumber`, the anti-cache probe this cluster standardized on, so it cannot be measured on equal terms. Exclusions follow the cluster-wide rules documented on the parent benchmark." findings: - "{{best_name}} currently leads free BNB Chain RPC at {{best_p50}} (`eth_getBlockByNumber` p50, 24h) across 5 measured providers." @@ -36,7 +36,7 @@ faq: - q: "What is the fastest free BNB Chain RPC right now?" a: "{{best_name}} currently leads at {{best_p50}} (`eth_getBlockByNumber` p50 over the last 24h), measured against 5 no-key providers probed every 60 seconds from us-east, eu-west and Singapore. The leaderboard re-sorts continuously against fresh Prometheus samples, so the answer on this page is the answer right now, not a quarterly snapshot. Use the region tabs to see the leader from the origin closest to your deployment." - q: "Which BNB Chain RPCs work without an API key?" - a: "The 5 providers on this page: PublicNode, dRPC, Nodies, Merkle, Binance. Merkle blocks datacenter IPs since July 2026, so its probes route through a residential proxy; the latency column includes that proxy overhead and the row is flagged accordingly. Every (provider, chain) pair was live-verified no-key before inclusion, and anything that key-gates, region-blocks or rate-limits below our 15-second cadence is excluded rather than listed with an asterisk." + a: "The 4 providers on this page: PublicNode, dRPC, Nodies, Binance. Merkle was delisted in July 2026 because its gateway does not serve `eth_getBlockByNumber` (JSON-RPC -32601), the anti-cache probe used across the cluster, so its latency cannot be compared on equal terms. Every (provider, chain) pair was live-verified no-key before inclusion, and anything that key-gates, region-blocks or rate-limits below our 15-second cadence is excluded rather than listed with an asterisk." - q: "Does the fastest BNB Chain RPC change by region?" a: "Frequently. The headline number averages three probe origins (us-east, eu-west, Singapore), but per-region leaders regularly diverge, a gateway that wins from Virginia can lose from Singapore by multiples. The region tabs at the top of the page re-scope every number on the page to a single origin; pick the one closest to where your requests actually originate." - q: "How is BNB Chain RPC latency measured here?" @@ -132,29 +132,6 @@ providers: p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="nodies", chain="bnb", region="sgp"}) series: avg_over_time(rpc_latency_milliseconds{provider="nodies", chain="bnb", region="sgp"}[1h]) - - slug: merkle - name: Merkle - tag: Blocks datacenter IPs since 2026-07; probed via residential proxy, latency includes proxy overhead - formula: "50th percentile over 24h of round-trip latency (ms) for `eth_getBlockByNumber` every 60s from 3 regions via a residential proxy (Merkle blocks datacenter IPs since 2026-07); includes proxy overhead, not directly comparable." - queries: - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="bnb"}) - p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="merkle", chain="bnb"}) - p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="merkle", chain="bnb"}) - mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="merkle", chain="bnb"}) - success: sum(ocb:rpc_call:ok_rate_24h{provider="merkle", chain="bnb"}) / sum(ocb:rpc_call:rate_24h{provider="merkle", chain="bnb"}) - sample_size: sum(ocb:rpc_call:increase_24h{provider="merkle", chain="bnb"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="bnb"}[1h])) - regions: - - region: us-east - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="bnb", region="us-east"}) - series: avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="bnb", region="us-east"}[1h]) - - region: eu-west - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="bnb", region="eu-west"}) - series: avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="bnb", region="eu-west"}[1h]) - - region: ap-southeast - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="merkle", chain="bnb", region="sgp"}) - series: avg_over_time(rpc_latency_milliseconds{provider="merkle", chain="bnb", region="sgp"}[1h]) - - slug: binance name: Binance tag: BNB Chain dataseed RPC, Binance-operated diff --git a/harnesses/rpc-capabilities/cmd/script/config.go b/harnesses/rpc-capabilities/cmd/script/config.go index 33761881..d23fe91d 100644 --- a/harnesses/rpc-capabilities/cmd/script/config.go +++ b/harnesses/rpc-capabilities/cmd/script/config.go @@ -23,11 +23,6 @@ type Provider struct { Slug string Name string URL string - // ViaProxy routes probes through RPC_PROBE_PROXY_URL (residential - // rotating proxy). For providers that block datacenter IPs (merkle - // since 2026-07). Latency then includes proxy overhead; the spec - // discloses it on the row. - ViaProxy bool } type Chain struct { @@ -148,7 +143,6 @@ func chains() []Chain { {Slug: "drpc", Name: "dRPC", URL: envDefault("RPC_URL_BASE_DRPC", "https://base.drpc.org")}, {Slug: "tenderly", Name: "Tenderly Gateway", URL: envDefault("RPC_URL_BASE_TENDERLY", "https://gateway.tenderly.co/public/base")}, {Slug: "nodies", Name: "Nodies (POKT)", URL: envDefault("RPC_URL_BASE_NODIES", "https://base-pokt.nodies.app")}, - {Slug: "merkle", Name: "Merkle", URL: envDefault("RPC_URL_BASE_MERKLE", "https://base.merkle.io"), ViaProxy: true}, {Slug: "base-official", Name: "Base Official", URL: envDefault("RPC_URL_BASE_OFFICIAL", "https://mainnet.base.org")}, }, }, @@ -160,7 +154,6 @@ func chains() []Chain { {Slug: "publicnode", Name: "PublicNode", URL: envDefault("RPC_URL_BNB_PUBLICNODE", "https://bsc-rpc.publicnode.com")}, {Slug: "drpc", Name: "dRPC", URL: envDefault("RPC_URL_BNB_DRPC", "https://bsc.drpc.org")}, {Slug: "nodies", Name: "Nodies (POKT)", URL: envDefault("RPC_URL_BNB_NODIES", "https://bsc-pokt.nodies.app")}, - {Slug: "merkle", Name: "Merkle", URL: envDefault("RPC_URL_BNB_MERKLE", "https://bsc.merkle.io"), ViaProxy: true}, {Slug: "binance", Name: "Binance Official", URL: envDefault("RPC_URL_BNB_OFFICIAL", "https://bsc-dataseed1.binance.org")}, }, }, diff --git a/harnesses/rpc-capabilities/cmd/script/probe.go b/harnesses/rpc-capabilities/cmd/script/probe.go index eb0e1308..620eee81 100644 --- a/harnesses/rpc-capabilities/cmd/script/probe.go +++ b/harnesses/rpc-capabilities/cmd/script/probe.go @@ -6,49 +6,13 @@ import ( "encoding/json" "fmt" "io" - "log" "net/http" - "net/url" - "os" "strconv" "strings" "sync" "time" ) -// probeProxyClient returns a client routing through RPC_PROBE_PROXY_URL -// (residential rotating proxy) for providers that block datacenter IPs. -// Falls back to a direct client if the env is unset, which surfaces as a -// visible 100% error rate rather than a silent measurement change. -var proxyClientOnce sync.Once -var proxyClientCached *http.Client - -func probeProxyClient() *http.Client { - proxyClientOnce.Do(func() { - raw := strings.TrimSpace(os.Getenv("RPC_PROBE_PROXY_URL")) - if raw == "" { - log.Printf("[PROBE] WARNING: ViaProxy provider configured but RPC_PROBE_PROXY_URL is unset; probing direct") - proxyClientCached = &http.Client{Timeout: probeTimeout} - return - } - u, err := url.Parse(raw) - if err != nil { - log.Printf("[PROBE] WARNING: invalid RPC_PROBE_PROXY_URL: %v; probing direct", err) - proxyClientCached = &http.Client{Timeout: probeTimeout} - return - } - proxyClientCached = &http.Client{ - Timeout: probeTimeout, - Transport: &http.Transport{ - Proxy: http.ProxyURL(u), - DisableKeepAlives: true, // fresh proxy IP per probe - MaxIdleConnsPerHost: 0, - }, - } - }) - return proxyClientCached -} - const ( // 60s (was 30s): the cluster probes ~23 chains per provider from ONE // IP per region. Keyless per-IP budgets (1RPC daily cap, dRPC CU/s) @@ -128,7 +92,7 @@ type blockHeader struct { // on cache hits rather than real RPC work. Fetching the full latest // header with a rotating request id defeats body-keyed edge caches; // the header's `number` field keeps the staleness check intact. -func callLatestBlock(ctx context.Context, url string, viaProxy bool) (block uint64, result string, latencyMs float64, err error) { +func callLatestBlock(ctx context.Context, url string) (block uint64, result string, latencyMs float64, err error) { body := []byte(fmt.Sprintf( `{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["latest",false],"id":%d}`, time.Now().UnixNano(), @@ -137,9 +101,6 @@ func callLatestBlock(ctx context.Context, url string, viaProxy bool) (block uint req.Header.Set("Content-Type", "application/json") req.Header.Set("User-Agent", "OpenChainBench/1.0 (+https://openchainbench.com)") client := &http.Client{Timeout: probeTimeout} - if viaProxy { - client = probeProxyClient() - } start := time.Now() resp, err := client.Do(req) @@ -214,7 +175,7 @@ func probeOne(ctx context.Context, c Chain, p Provider) { tick := func() { probeCtx, cancel := context.WithTimeout(ctx, probeTimeout) defer cancel() - block, result, latency, err := callLatestBlock(probeCtx, p.URL, p.ViaProxy) + block, result, latency, err := callLatestBlock(probeCtx, p.URL) if result == "ok" { tips.update(c.Slug, block)