diff --git a/.agents/skills/obol-stack-dev/references/litellm-routing.md b/.agents/skills/obol-stack-dev/references/litellm-routing.md index 3f00807b..0f452430 100644 --- a/.agents/skills/obol-stack-dev/references/litellm-routing.md +++ b/.agents/skills/obol-stack-dev/references/litellm-routing.md @@ -13,7 +13,7 @@ A cluster-wide OpenAI-compatible proxy that routes LLM traffic to actual provide | `llm` | Namespace | Dedicated namespace for LLM infrastructure | | `litellm-config` | ConfigMap | `config.yaml` with `model_list` (model definitions + routing) | | `litellm-secrets` | Secret | `LITELLM_MASTER_KEY`, `ANTHROPIC_API_KEY`, `OPENAI_API_KEY` | -| `litellm` | Deployment | `ghcr.io/berriai/litellm:main-stable`, port 4000 | +| `litellm` | Deployment | `ghcr.io/berriai/litellm:main-v1.82.3`, port 4000 | | `litellm` | Service | `litellm.llm.svc.cluster.local:4000` | | `ollama` | Service (ExternalName) | Routes to host Ollama | diff --git a/Dockerfile.reth-erc8004-indexer b/Dockerfile.reth-erc8004-indexer new file mode 100644 index 00000000..81019e2b --- /dev/null +++ b/Dockerfile.reth-erc8004-indexer @@ -0,0 +1,12 @@ +FROM rust:1.93-bookworm AS builder + +WORKDIR /build + +COPY reth-erc8004-indexer/Cargo.toml reth-erc8004-indexer/Cargo.toml +COPY reth-erc8004-indexer/src reth-erc8004-indexer/src + +RUN cargo build --release --manifest-path reth-erc8004-indexer/Cargo.toml + +FROM ghcr.io/paradigmxyz/reth:v1.11.1 + +COPY --from=builder /build/target/release/reth /usr/local/bin/reth diff --git a/Dockerfile.worker b/Dockerfile.worker new file mode 100644 index 00000000..6172666d --- /dev/null +++ b/Dockerfile.worker @@ -0,0 +1,33 @@ +FROM nvidia/cuda:12.4.1-runtime-ubuntu22.04 + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + curl \ + git \ + python3 \ + python3-pip \ + python3-venv \ + && rm -rf /var/lib/apt/lists/* + +# Install uv for running the autoresearch repo environment. +RUN curl -LsSf https://astral.sh/uv/install.sh | sh \ + && mv /root/.local/bin/uv /usr/local/bin/uv + +WORKDIR /app + +COPY internal/embed/skills/autoresearch-worker/scripts/worker_api.py /app/worker_api.py + +ENV DATA_DIR=/data \ + AUTORESEARCH_REPO=/data/autoresearch \ + EXPERIMENT_TIMEOUT_SECONDS=300 \ + TRAIN_COMMAND="uv run train.py" + +RUN useradd -m -s /bin/bash worker && mkdir -p /data && chown worker:worker /data + +USER worker +VOLUME ["/data"] +EXPOSE 8080 + +ENTRYPOINT ["python3", "/app/worker_api.py", "serve", "--repo", "/data/autoresearch", "--data-dir", "/data", "--host", "0.0.0.0", "--port", "8080"] diff --git a/cmd/obol/model.go b/cmd/obol/model.go index f1c335df..05063243 100644 --- a/cmd/obol/model.go +++ b/cmd/obol/model.go @@ -213,7 +213,7 @@ func modelSetupCustomCommand(cfg *config.Config) *cli.Command { Action: func(ctx context.Context, cmd *cli.Command) error { u := getUI(cmd) name := cmd.String("name") - endpoint := model.WarnAndStripV1Suffix(cmd.String("endpoint")) + endpoint := cmd.String("endpoint") modelName := cmd.String("model") apiKey := cmd.String("api-key") diff --git a/cmd/obol/sell.go b/cmd/obol/sell.go index 36978a82..3c8ef320 100644 --- a/cmd/obol/sell.go +++ b/cmd/obol/sell.go @@ -1,11 +1,14 @@ package main import ( + "bytes" "context" "encoding/hex" "encoding/json" "fmt" + "io" "net" + "net/http" "os" "os/signal" "runtime" @@ -37,6 +40,7 @@ func sellCommand(cfg *config.Config) *cli.Command { sellHTTPCommand(cfg), sellListCommand(cfg), sellStatusCommand(cfg), + sellProbeCommand(cfg), sellStopCommand(cfg), sellDeleteCommand(cfg), sellPricingCommand(cfg), @@ -146,6 +150,10 @@ Examples: Usage: "SHA-256 of model weights for TEE attestation (required with --tee)", Sources: cli.EnvVars("OBOL_MODEL_HASH"), }, + &cli.StringFlag{ + Name: "provenance-file", + Usage: "Path to JSON file with provenance metadata (e.g. autoresearch experiment results)", + }, }, Action: func(ctx context.Context, cmd *cli.Command) error { name := cmd.Args().First() @@ -204,6 +212,16 @@ Examples: TEEType: teeType, ModelHash: modelHash, } + + if pf := cmd.String("provenance-file"); pf != "" { + prov, err := loadProvenance(pf) + if err != nil { + return fmt.Errorf("load provenance: %w", err) + } + d.Provenance = prov + fmt.Printf("Loaded provenance: %s (metric %s=%s, params %s)\n", + prov.Framework, prov.MetricName, prov.MetricValue, prov.ParamCount) + } if priceTable.PerMTok != "" { d.ApproxTokensPerRequest = schemas.ApproxTokensPerRequest } @@ -379,6 +397,14 @@ Examples: Name: "register-domains", Usage: "OASF domains for discovery (e.g. technology/artificial_intelligence)", }, + &cli.StringSliceFlag{ + Name: "register-metadata", + Usage: "Additional registration metadata as key=value pairs (repeatable, e.g. gpu=A100-80GB)", + }, + &cli.StringFlag{ + Name: "provenance-file", + Usage: "Path to JSON file with provenance metadata (e.g. autoresearch experiment results)", + }, }, Action: func(ctx context.Context, cmd *cli.Command) error { if cmd.NArg() == 0 { @@ -422,6 +448,25 @@ Examples: spec["path"] = path } + if pf := cmd.String("provenance-file"); pf != "" { + prov, err := loadProvenance(pf) + if err != nil { + return fmt.Errorf("load provenance: %w", err) + } + // Round-trip through JSON to build the map, respecting omitempty tags. + provBytes, err := json.Marshal(prov) + if err != nil { + return fmt.Errorf("marshal provenance: %w", err) + } + var provMap map[string]interface{} + if err := json.Unmarshal(provBytes, &provMap); err != nil { + return fmt.Errorf("unmarshal provenance: %w", err) + } + spec["provenance"] = provMap + fmt.Printf("Loaded provenance: %s (metric %s=%s, params %s)\n", + prov.Framework, prov.MetricName, prov.MetricValue, prov.ParamCount) + } + if cmd.Bool("register") || cmd.String("register-name") != "" { reg := map[string]interface{}{ "enabled": cmd.Bool("register"), @@ -441,6 +486,13 @@ Examples: if domains := cmd.StringSlice("register-domains"); len(domains) > 0 { reg["domains"] = domains } + if metaPairs := cmd.StringSlice("register-metadata"); len(metaPairs) > 0 { + meta, err := parseMetadataPairs(metaPairs) + if err != nil { + return err + } + reg["metadata"] = meta + } spec["registration"] = reg } @@ -580,6 +632,101 @@ func sellStatusCommand(cfg *config.Config) *cli.Command { } } +// --------------------------------------------------------------------------- +// sell probe — send an unauthenticated request to verify 402 payment gate +// --------------------------------------------------------------------------- + +func sellProbeCommand(cfg *config.Config) *cli.Command { + return &cli.Command{ + Name: "probe", + Usage: "Probe a ServiceOffer endpoint to verify it returns 402 pricing", + ArgsUsage: "", + Description: `Sends an unauthenticated request through Traefik to the ServiceOffer's +endpoint and displays the HTTP status code and x402 pricing response. + +A 402 response with x402Version=1 confirms the endpoint is live and payment-gated. + +Examples: + obol sell probe flow-qwen -n llm + obol sell probe my-api -n default --path /health`, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "namespace", + Aliases: []string{"n"}, + Usage: "Namespace of the ServiceOffer", + }, + &cli.StringFlag{ + Name: "path", + Usage: "Subpath to probe (appended to the offer's endpoint)", + Value: "/health", + }, + &cli.StringFlag{ + Name: "host", + Usage: "Traefik host:port", + Value: "obol.stack:8080", + }, + }, + Action: func(ctx context.Context, cmd *cli.Command) error { + name := cmd.Args().First() + if name == "" { + return fmt.Errorf("name required: obol sell probe -n ") + } + ns := cmd.String("namespace") + if ns == "" { + return fmt.Errorf("namespace required: obol sell probe -n ") + } + + // Get the ServiceOffer's endpoint from the CR status. + endpoint, err := kubectlOutput(cfg, "get", "serviceoffers.obol.org", name, + "-n", ns, "-o", "jsonpath={.status.endpoint}") + if err != nil { + return fmt.Errorf("get ServiceOffer %s/%s: %w", ns, name, err) + } + endpoint = strings.TrimSpace(endpoint) + if endpoint == "" { + return fmt.Errorf("ServiceOffer %s/%s has no endpoint (not yet reconciled?)", ns, name) + } + + subpath := cmd.String("path") + probeURL := "http://" + cmd.String("host") + endpoint + subpath + fmt.Printf("Probing %s ...\n", probeURL) + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, probeURL, nil) + if err != nil { + return fmt.Errorf("create request: %w", err) + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return fmt.Errorf("probe failed: %w", err) + } + defer resp.Body.Close() + + body, _ := io.ReadAll(resp.Body) + fmt.Printf("HTTP %d\n", resp.StatusCode) + + if resp.StatusCode == http.StatusPaymentRequired { + var pretty bytes.Buffer + if json.Indent(&pretty, body, "", " ") == nil { + fmt.Println(pretty.String()) + } else { + fmt.Println(string(body)) + } + fmt.Println("\nEndpoint is live and payment-gated.") + return nil + } + + if len(body) > 0 { + fmt.Println(string(body)) + } + if resp.StatusCode == http.StatusOK { + fmt.Println("\nWarning: endpoint returned 200 (not payment-gated).") + } + return nil + }, + } +} + // --------------------------------------------------------------------------- // sell stop // --------------------------------------------------------------------------- @@ -1054,6 +1201,18 @@ func valueOrNone(s string) string { return s } +func parseMetadataPairs(values []string) (map[string]string, error) { + meta := make(map[string]string, len(values)) + for _, raw := range values { + key, value, ok := strings.Cut(raw, "=") + if !ok || strings.TrimSpace(key) == "" { + return nil, fmt.Errorf("invalid --register-metadata value %q: expected key=value", raw) + } + meta[strings.TrimSpace(key)] = strings.TrimSpace(value) + } + return meta, nil +} + func resolvePriceTable(cmd *cli.Command, allowPerHour bool) (schemas.PriceTable, error) { perRequest := cmd.String("price") if perRequest == "" { @@ -1074,6 +1233,9 @@ func resolvePriceTable(cmd *cli.Command, allowPerHour bool) (schemas.PriceTable, } return schemas.PriceTable{PerMTok: perMTok}, nil case perHour != "": + if _, err := schemas.ApproximateRequestPriceFromPerHour(perHour); err != nil { + return schemas.PriceTable{}, fmt.Errorf("invalid --per-hour value %q: %w", perHour, err) + } return schemas.PriceTable{PerHour: perHour}, nil default: if allowPerHour { @@ -1094,7 +1256,11 @@ func formatPriceTableSummary(priceTable schemas.PriceTable) string { schemas.ApproxTokensPerRequest, ) case priceTable.PerHour != "": - return fmt.Sprintf("%s USDC/hour", priceTable.PerHour) + return fmt.Sprintf("%s USDC/request (approx from %s USDC/hour @ %d min/request)", + priceTable.EffectiveRequestPrice(), + priceTable.PerHour, + schemas.ApproxMinutesPerRequest, + ) default: return "0 USDC/request" } @@ -1119,6 +1285,21 @@ func formatInferencePriceSummary(d *inference.Deployment) string { return fmt.Sprintf("%s USDC/request", d.PricePerRequest) } +// loadProvenance reads a provenance JSON file and returns the parsed struct. +func loadProvenance(path string) (*inference.Provenance, error) { + data, err := os.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("read %s: %w", path, err) + } + var prov inference.Provenance + dec := json.NewDecoder(bytes.NewReader(data)) + dec.DisallowUnknownFields() + if err := dec.Decode(&prov); err != nil { + return nil, fmt.Errorf("parse %s: %w", path, err) + } + return &prov, nil +} + // createHostService creates a headless Service + Endpoints in the cluster // pointing to the Docker host IP on the given port, so that the cluster can // route traffic to a host-side inference gateway. diff --git a/flows/flow-06-sell-setup.sh b/flows/flow-06-sell-setup.sh new file mode 100755 index 00000000..f932821e --- /dev/null +++ b/flows/flow-06-sell-setup.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +# flow-06-sell-setup.sh — Create a ServiceOffer for Ollama inference via obol sell http +set -euo pipefail + +# ── Configuration ────────────────────────────────────────────────────────── +OFFER_NAME="${OFFER_NAME:-flow-qwen}" +OFFER_NS="${OFFER_NS:-llm}" +UPSTREAM_SVC="${UPSTREAM_SVC:-ollama}" +UPSTREAM_PORT="${UPSTREAM_PORT:-11434}" +HEALTH_PATH="${HEALTH_PATH:-/health}" +# Anvil deterministic account #1 (seller wallet) +WALLET="${X402_WALLET:-0x70997970C51812dc3A010C7d01b50e0d17dc79C8}" +CHAIN="${CHAIN:-base-sepolia}" +PRICE="${PRICE:-0.001}" + +# Resolve obol binary (prefer workspace binary in dev mode) +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +OBOL="${OBOL_BIN:-$ROOT_DIR/.workspace/bin/obol}" +export OBOL_DEVELOPMENT="${OBOL_DEVELOPMENT:-true}" +export OBOL_CONFIG_DIR="${OBOL_CONFIG_DIR:-$ROOT_DIR/.workspace/config}" +export OBOL_BIN_DIR="${OBOL_BIN_DIR:-$ROOT_DIR/.workspace/bin}" +export OBOL_DATA_DIR="${OBOL_DATA_DIR:-$ROOT_DIR/.workspace/data}" +export KUBECONFIG="${OBOL_CONFIG_DIR}/kubeconfig.yaml" + +echo "=== flow-06-sell-setup ===" +echo "Offer: $OFFER_NAME (ns: $OFFER_NS)" +echo "Upstream: $UPSTREAM_SVC:$UPSTREAM_PORT" +echo "Wallet: $WALLET" +echo "Price: $PRICE USDC/request on $CHAIN" +echo "" + +# ── Step 1: Check if offer already exists and is Ready ───────────────────── +EXISTING_READY=$(kubectl get serviceoffer "$OFFER_NAME" -n "$OFFER_NS" \ + -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}' 2>/dev/null || echo "") + +if [ "$EXISTING_READY" = "True" ]; then + echo "ServiceOffer $OFFER_NAME already exists and is Ready — using existing offer." + echo "(Delete with: $OBOL sell delete $OFFER_NAME -n $OFFER_NS --force)" + echo "" + echo "=== flow-06-sell-setup PASSED ===" + exit 0 +fi + +# ── Step 2: Clean up any non-Ready existing offer ───────────────────────── +if "$OBOL" sell list 2>/dev/null | grep -q "$OFFER_NAME"; then + echo "Deleting non-Ready ServiceOffer $OFFER_NAME..." + "$OBOL" sell delete "$OFFER_NAME" -n "$OFFER_NS" --force 2>/dev/null || true + sleep 3 +fi + +# ── Step 3: Ensure pricing is configured ─────────────────────────────────── +echo "Configuring x402 pricing..." +"$OBOL" sell pricing --wallet "$WALLET" --chain "$CHAIN" + +# ── Step 4: Create the ServiceOffer ──────────────────────────────────────── +echo "" +echo "Creating ServiceOffer..." +"$OBOL" sell http "$OFFER_NAME" \ + --wallet "$WALLET" \ + --chain "$CHAIN" \ + --price "$PRICE" \ + --namespace "$OFFER_NS" \ + --upstream "$UPSTREAM_SVC" \ + --port "$UPSTREAM_PORT" \ + --health-path "$HEALTH_PATH" + +echo "" +echo "ServiceOffer created. Waiting for agent to reconcile..." +echo "Check status: $OBOL sell status $OFFER_NAME -n $OFFER_NS" +echo "" +echo "=== flow-06-sell-setup PASSED ===" diff --git a/flows/flow-07-sell-verify.sh b/flows/flow-07-sell-verify.sh new file mode 100755 index 00000000..1cb65015 --- /dev/null +++ b/flows/flow-07-sell-verify.sh @@ -0,0 +1,98 @@ +#!/usr/bin/env bash +# flow-07-sell-verify.sh — Wait for ServiceOffer reconciliation and verify 402 payment gate +set -euo pipefail + +# ── Configuration ────────────────────────────────────────────────────────── +OFFER_NAME="${OFFER_NAME:-flow-qwen}" +OFFER_NS="${OFFER_NS:-llm}" +# Agent heartbeat can be up to 30 min; default 35 min timeout +MAX_WAIT="${MAX_WAIT:-2100}" + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +OBOL="${OBOL_BIN:-$ROOT_DIR/.workspace/bin/obol}" +export OBOL_DEVELOPMENT="${OBOL_DEVELOPMENT:-true}" +export OBOL_CONFIG_DIR="${OBOL_CONFIG_DIR:-$ROOT_DIR/.workspace/config}" +export OBOL_BIN_DIR="${OBOL_BIN_DIR:-$ROOT_DIR/.workspace/bin}" +export OBOL_DATA_DIR="${OBOL_DATA_DIR:-$ROOT_DIR/.workspace/data}" +export KUBECONFIG="${OBOL_CONFIG_DIR}/kubeconfig.yaml" + +echo "=== flow-07-sell-verify ===" +echo "Waiting for ServiceOffer $OFFER_NAME to reach Ready (max ${MAX_WAIT}s)..." +echo "" + +# ── Step 1: Wait for Ready condition ─────────────────────────────────────── +elapsed=0 +while [ "$elapsed" -lt "$MAX_WAIT" ]; do + ready=$(kubectl get serviceoffer "$OFFER_NAME" -n "$OFFER_NS" \ + -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}' 2>/dev/null || echo "") + + if [ "$ready" = "True" ]; then + echo "" + echo "ServiceOffer $OFFER_NAME is Ready (after ${elapsed}s)" + break + fi + + # Show current phase + phase=$(kubectl get serviceoffer "$OFFER_NAME" -n "$OFFER_NS" \ + -o jsonpath='{.status.conditions[*].type}' 2>/dev/null || echo "pending") + if [ -z "$phase" ]; then phase="(no conditions — waiting for agent heartbeat)"; fi + printf "\r [%3ds] %s " "$elapsed" "$phase" + + sleep 15 + elapsed=$((elapsed + 15)) +done + +if [ "$elapsed" -ge "$MAX_WAIT" ]; then + echo "" + echo "TIMEOUT: ServiceOffer did not reach Ready in ${MAX_WAIT}s" + "$OBOL" sell status "$OFFER_NAME" -n "$OFFER_NS" 2>&1 || true + exit 1 +fi + +echo "" + +# ── Step 2: Verify all 6 conditions ─────────────────────────────────────── +echo "Checking all conditions..." +conditions=("ModelReady" "UpstreamHealthy" "PaymentGateReady" "RoutePublished" "Registered" "Ready") +all_ok=true +for cond in "${conditions[@]}"; do + status=$(kubectl get serviceoffer "$OFFER_NAME" -n "$OFFER_NS" \ + -o jsonpath="{.status.conditions[?(@.type==\"$cond\")].status}" 2>/dev/null || echo "") + message=$(kubectl get serviceoffer "$OFFER_NAME" -n "$OFFER_NS" \ + -o jsonpath="{.status.conditions[?(@.type==\"$cond\")].message}" 2>/dev/null || echo "") + if [ "$status" = "True" ]; then + echo " [OK] $cond: $message" + else + echo " [FAIL] $cond: status=$status message=$message" + all_ok=false + fi +done + +if [ "$all_ok" != "true" ]; then + echo "" + echo "FAIL: Not all conditions are met" + exit 1 +fi + +echo "" + +# ── Step 3: Probe the endpoint ───────────────────────────────────────────── +echo "Probing endpoint..." +"$OBOL" sell probe "$OFFER_NAME" -n "$OFFER_NS" + +echo "" + +# ── Step 4: Verify x402-pricing ConfigMap has a route ────────────────────── +echo "Checking x402-pricing ConfigMap..." +route_count=$(kubectl get cm x402-pricing -n x402 -o jsonpath='{.data.pricing\.yaml}' 2>/dev/null \ + | grep -c "pattern:.*$OFFER_NAME" || echo "0") +if [ "$route_count" -gt 0 ]; then + echo " [OK] Pricing route for $OFFER_NAME found in x402-pricing ConfigMap" +else + echo " [FAIL] No pricing route for $OFFER_NAME in x402-pricing ConfigMap" + exit 1 +fi + +echo "" +echo "=== flow-07-sell-verify PASSED ===" diff --git a/flows/flow-08-buy.sh b/flows/flow-08-buy.sh new file mode 100755 index 00000000..1fc9d920 --- /dev/null +++ b/flows/flow-08-buy.sh @@ -0,0 +1,202 @@ +#!/usr/bin/env bash +# flow-08-buy.sh — Probe a 402 endpoint, sign ERC-3009 payment, send paid request +# +# Prerequisites: +# - flow-06 + flow-07 completed (ServiceOffer is Ready) +# - flow-10 completed (Anvil + Facilitator running, /tmp/m1-infra.env exists) +# - cast (Foundry) +# - python3 (for JSON parsing) +set -euo pipefail + +# ── Configuration ────────────────────────────────────────────────────────── +OFFER_NAME="${OFFER_NAME:-flow-qwen}" +OFFER_NS="${OFFER_NS:-llm}" +HOST="${HOST:-obol.stack:8080}" +MODEL="${MODEL:-qwen3.5:9b}" +USDC_CONTRACT="0x036CbD53842c5426634e7929541eC2318f3dCF7e" +CHAIN_ID=84532 # Base Sepolia + +# Load infrastructure state from flow-10 +if [ -f /tmp/m1-infra.env ]; then + source /tmp/m1-infra.env +else + echo "WARN: /tmp/m1-infra.env not found (flow-10 not run?)" + BUYER_KEY="${BUYER_KEY:-5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a}" + BUYER_ADDR="${BUYER_ADDR:-0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC}" +fi + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +OBOL="${OBOL_BIN:-$ROOT_DIR/.workspace/bin/obol}" +export OBOL_DEVELOPMENT="${OBOL_DEVELOPMENT:-true}" +export OBOL_CONFIG_DIR="${OBOL_CONFIG_DIR:-$ROOT_DIR/.workspace/config}" +export OBOL_BIN_DIR="${OBOL_BIN_DIR:-$ROOT_DIR/.workspace/bin}" +export OBOL_DATA_DIR="${OBOL_DATA_DIR:-$ROOT_DIR/.workspace/data}" +export KUBECONFIG="${OBOL_CONFIG_DIR}/kubeconfig.yaml" + +echo "=== flow-08-buy ===" + +# ── Step 1: Probe the endpoint ───────────────────────────────────────────── +echo "Step 1: Probing for 402 pricing..." +ENDPOINT=$("$OBOL" kubectl get serviceoffer "$OFFER_NAME" -n "$OFFER_NS" \ + -o 'jsonpath={.status.endpoint}') +PROBE_URL="http://$HOST${ENDPOINT}/v1/chat/completions" + +BODY_FILE=$(mktemp /tmp/probe-body-XXXXXXXX) +HTTP_CODE=$(curl -s -o "$BODY_FILE" -w "%{http_code}" "$PROBE_URL" 2>&1 || echo "000") +BODY=$(cat "$BODY_FILE") + +if [ "$HTTP_CODE" != "402" ]; then + echo " FAIL: Expected HTTP 402, got $HTTP_CODE" + echo " Body: $BODY" + exit 1 +fi + +echo " Got HTTP 402" + +# Parse pricing from 402 response +PAY_TO=$(echo "$BODY" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['accepts'][0]['payTo'])") +AMOUNT=$(echo "$BODY" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['accepts'][0]['maxAmountRequired'])") +NETWORK=$(echo "$BODY" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['accepts'][0]['network'])") +RESOURCE=$(echo "$BODY" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['accepts'][0]['resource'])") + +rm -f "$BODY_FILE" +echo " PayTo: $PAY_TO" +echo " Amount: $AMOUNT" +echo " Network: $NETWORK" +echo " Resource: $RESOURCE" +echo "" + +# ── Step 2: Sign ERC-3009 TransferWithAuthorization ──────────────────────── +echo "Step 2: Signing ERC-3009 payment..." + +NONCE="0x$(openssl rand -hex 32)" + +# Build EIP-712 typed data JSON +TYPED_DATA=$(cat < "$TYPED_DATA_FILE" + +SIGNATURE=$(cast wallet sign --private-key "0x$BUYER_KEY" --data --from-file "$TYPED_DATA_FILE" 2>&1) +rm -f "$TYPED_DATA_FILE" + +echo " Signature: ${SIGNATURE:0:20}..." +echo "" + +# ── Step 3: Build x402 payment envelope ──────────────────────────────────── +echo "Step 3: Building x402 payment envelope..." + +ENVELOPE=$(python3 -c " +import json, base64 +envelope = { + 'x402Version': 1, + 'scheme': 'exact', + 'network': '$NETWORK', + 'payload': { + 'signature': '$SIGNATURE', + 'authorization': { + 'from': '$BUYER_ADDR', + 'to': '$PAY_TO', + 'value': '$AMOUNT', + 'validAfter': '0', + 'validBefore': '4294967295', + 'nonce': '$NONCE' + } + }, + 'resource': { + 'payTo': '$PAY_TO', + 'maxAmountRequired': '$AMOUNT', + 'asset': '$USDC_CONTRACT', + 'network': '$NETWORK' + } +} +print(base64.b64encode(json.dumps(envelope).encode()).decode()) +") + +echo " Envelope: ${ENVELOPE:0:40}..." +echo "" + +# ── Step 4: Send paid request ────────────────────────────────────────────── +echo "Step 4: Sending paid request to $PROBE_URL ..." + +PAID_BODY_FILE=$(mktemp /tmp/paid-body-XXXXXXXX) +PAID_CODE=$(curl -s -o "$PAID_BODY_FILE" -w "%{http_code}" \ + -X POST "$PROBE_URL" \ + -H "Content-Type: application/json" \ + -H "X-PAYMENT: $ENVELOPE" \ + -d "{\"model\": \"$MODEL\", \"messages\": [{\"role\": \"user\", \"content\": \"Say hello\"}], \"max_tokens\": 20}" \ + 2>&1 || echo "000") +PAID_BODY=$(cat "$PAID_BODY_FILE") +rm -f "$PAID_BODY_FILE" + +echo " HTTP $PAID_CODE" + +if [ "$PAID_CODE" = "200" ]; then + echo " Response: $(echo "$PAID_BODY" | python3 -c " +import sys, json +try: + d = json.load(sys.stdin) + if 'choices' in d: + msg = d['choices'][0].get('message', {}) + c = msg.get('content', '') or msg.get('reasoning', '') or msg.get('reasoning_content', '') + print(c[:200] if c else '(model returned empty content)') + else: + print(json.dumps(d)[:200]) +except: + print(sys.stdin.read()[:200]) +" 2>/dev/null || echo "$PAID_BODY" | head -5)" + echo "" + echo "=== flow-08-buy PASSED ===" +elif [ "$PAID_CODE" = "402" ]; then + echo " FAIL: Payment was not accepted (still 402)" + echo " Body: $(echo "$PAID_BODY" | head -5)" + exit 1 +else + echo " Response: $(echo "$PAID_BODY" | head -5)" + # Non-402 might be OK (e.g. 500 from upstream if model not loaded) + # but we consider 200 as the success criteria + if [ "$PAID_CODE" -ge "200" ] && [ "$PAID_CODE" -lt "300" ]; then + echo "" + echo "=== flow-08-buy PASSED ===" + else + echo " FAIL: Unexpected response code $PAID_CODE" + exit 1 + fi +fi diff --git a/flows/flow-10-anvil-facilitator.sh b/flows/flow-10-anvil-facilitator.sh new file mode 100755 index 00000000..5cbe439f --- /dev/null +++ b/flows/flow-10-anvil-facilitator.sh @@ -0,0 +1,180 @@ +#!/usr/bin/env bash +# flow-10-anvil-facilitator.sh — Start Anvil fork + x402-rs facilitator, patch verifier +# +# Prerequisites: +# - anvil (Foundry) +# - x402-rs facilitator binary at $X402_FACILITATOR_BIN or ~/Development/R&D/x402-rs/target/release/x402-facilitator +# - cast (Foundry) for USDC minting +# - Running k3d cluster with x402-verifier deployed +set -euo pipefail + +# ── Configuration ────────────────────────────────────────────────────────── +ANVIL_PORT="${ANVIL_PORT:-8545}" +FACILITATOR_PORT="${FACILITATOR_PORT:-4040}" +FORK_URL="${BASE_SEPOLIA_RPC_URL:-https://sepolia.base.org}" +USDC_CONTRACT="0x036CbD53842c5426634e7929541eC2318f3dCF7e" + +# Anvil deterministic accounts +FACILITATOR_KEY="ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" # account #0 +BUYER_ADDR="0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC" # account #2 +BUYER_KEY="5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a" + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +export OBOL_DEVELOPMENT="${OBOL_DEVELOPMENT:-true}" +export OBOL_CONFIG_DIR="${OBOL_CONFIG_DIR:-$ROOT_DIR/.workspace/config}" +export OBOL_BIN_DIR="${OBOL_BIN_DIR:-$ROOT_DIR/.workspace/bin}" +export OBOL_DATA_DIR="${OBOL_DATA_DIR:-$ROOT_DIR/.workspace/data}" +export KUBECONFIG="${OBOL_CONFIG_DIR}/kubeconfig.yaml" +KUBECTL="${OBOL_BIN_DIR}/kubectl" + +# Discover facilitator binary +if [ -n "${X402_FACILITATOR_BIN:-}" ] && [ -x "$X402_FACILITATOR_BIN" ]; then + FACILITATOR_BIN="$X402_FACILITATOR_BIN" +else + X402_RS_DIR="${X402_RS_DIR:-$HOME/Development/R&D/x402-rs}" + FACILITATOR_BIN="$X402_RS_DIR/target/release/x402-facilitator" + if [ ! -x "$FACILITATOR_BIN" ]; then + FACILITATOR_BIN="$X402_RS_DIR/target/release/facilitator" + fi +fi + +echo "=== flow-10-anvil-facilitator ===" + +# ── Pre-checks ───────────────────────────────────────────────────────────── +for cmd in anvil cast; do + command -v "$cmd" >/dev/null 2>&1 || { echo "FAIL: $cmd not found"; exit 1; } +done +[ -x "$FACILITATOR_BIN" ] || { echo "FAIL: facilitator binary not found at $FACILITATOR_BIN"; exit 1; } + +# ── Step 1: Kill any existing anvil/facilitator on these ports ───────────── +echo "Cleaning up existing processes..." +lsof -ti ":$ANVIL_PORT" 2>/dev/null | xargs kill -9 2>/dev/null || true +lsof -ti ":$FACILITATOR_PORT" 2>/dev/null | xargs kill -9 2>/dev/null || true +sleep 1 + +# ── Step 2: Start Anvil fork ────────────────────────────────────────────── +echo "Starting Anvil fork of Base Sepolia on port $ANVIL_PORT..." +anvil \ + --fork-url "$FORK_URL" \ + --host 0.0.0.0 \ + --port "$ANVIL_PORT" \ + --silent & +ANVIL_PID=$! + +# Wait for Anvil to be ready +for i in $(seq 1 30); do + if cast block-number --rpc-url "http://127.0.0.1:$ANVIL_PORT" >/dev/null 2>&1; then + echo " Anvil ready (pid $ANVIL_PID)" + break + fi + sleep 1 +done +cast block-number --rpc-url "http://127.0.0.1:$ANVIL_PORT" >/dev/null 2>&1 || { + echo "FAIL: Anvil did not start" + exit 1 +} + +# ── Step 3: Clear contract code on Anvil deterministic accounts ──────────── +# Anvil's deterministic addresses have proxy contracts on Base Sepolia. +# USDC's SignatureChecker sees code → tries EIP-1271 instead of ecrecover. +echo "Clearing contract code on buyer address..." +cast rpc anvil_setCode "$BUYER_ADDR" "0x" --rpc-url "http://127.0.0.1:$ANVIL_PORT" >/dev/null + +# ── Step 4: Mint USDC to buyer ───────────────────────────────────────────── +echo "Minting 10 USDC to buyer $BUYER_ADDR..." +# USDC balance mapping slot: keccak256(abi.encode(address, 9)) +SLOT=$(cast keccak "$(cast abi-encode 'f(address,uint256)' "$BUYER_ADDR" 9)") +VALUE=$(printf '0x%064x' 10000000) # 10 USDC = 10,000,000 micro-units +cast rpc anvil_setStorageAt "$USDC_CONTRACT" "$SLOT" "$VALUE" \ + --rpc-url "http://127.0.0.1:$ANVIL_PORT" >/dev/null + +# Verify balance +BALANCE=$(cast call "$USDC_CONTRACT" "balanceOf(address)(uint256)" "$BUYER_ADDR" \ + --rpc-url "http://127.0.0.1:$ANVIL_PORT" 2>/dev/null || echo "0") +echo " Buyer USDC balance: $BALANCE (should be 10000000)" + +# ── Step 5: Start facilitator ───────────────────────────────────────────── +echo "Starting x402-rs facilitator on port $FACILITATOR_PORT..." +FACILITATOR_CONFIG="/tmp/x402-facilitator-flow.json" +rm -f "$FACILITATOR_CONFIG" +cat > "$FACILITATOR_CONFIG" </dev/null 2>&1; then + echo " Facilitator ready (pid $FACILITATOR_PID)" + break + fi + sleep 1 +done +curl -sf "http://127.0.0.1:$FACILITATOR_PORT/supported" >/dev/null 2>&1 || { + echo "FAIL: Facilitator did not start" + kill "$ANVIL_PID" 2>/dev/null || true + exit 1 +} + +# ── Step 6: Patch x402-verifier to use local facilitator ─────────────────── +CLUSTER_FACILITATOR_URL="http://host.docker.internal:$FACILITATOR_PORT" +echo "Patching x402-verifier facilitatorURL → $CLUSTER_FACILITATOR_URL" + +# Read current pricing YAML +CURRENT_YAML=$("$KUBECTL" --kubeconfig "$KUBECONFIG" get cm x402-pricing -n x402 \ + -o 'jsonpath={.data.pricing\.yaml}') + +# Replace facilitatorURL +UPDATED_YAML=$(echo "$CURRENT_YAML" | sed "s|facilitatorURL:.*|facilitatorURL: \"$CLUSTER_FACILITATOR_URL\"|") + +# Patch the ConfigMap +PATCH_JSON=$(python3 -c " +import json, sys +print(json.dumps({'data': {'pricing.yaml': sys.stdin.read()}})) +" <<< "$UPDATED_YAML") + +"$KUBECTL" --kubeconfig "$KUBECONFIG" patch cm x402-pricing -n x402 \ + --type=merge -p="$PATCH_JSON" + +# Restart verifier to pick up new config +"$KUBECTL" --kubeconfig "$KUBECONFIG" rollout restart deploy/x402-verifier -n x402 +echo " Waiting for verifier restart..." +"$KUBECTL" --kubeconfig "$KUBECONFIG" rollout status deploy/x402-verifier -n x402 --timeout=60s + +echo "" +echo "Infrastructure ready:" +echo " Anvil: http://127.0.0.1:$ANVIL_PORT (pid $ANVIL_PID)" +echo " Facilitator: http://127.0.0.1:$FACILITATOR_PORT (pid $FACILITATOR_PID)" +echo " Buyer: $BUYER_ADDR (10 USDC)" +echo "" + +# Write PID file for cleanup +cat > /tmp/m1-infra.env <- + Optional provenance metadata for the service. Tracks how the + model or service was produced (e.g. autoresearch experiment data). + Included in the ERC-8004 registration document when present. + properties: + framework: + type: string + description: "Optimization framework (e.g. autoresearch)." + metricName: + type: string + description: "Name of the primary quality metric (e.g. val_bpb)." + metricValue: + type: string + description: "Primary quality metric value (e.g. 0.9973)." + experimentId: + type: string + description: "Experiment or commit identifier." + trainHash: + type: string + description: "SHA-256 hash of the training code that produced this model." + paramCount: + type: string + description: "Model parameter count (e.g. 50M, 1.3B)." path: type: string description: "URL path prefix for the HTTPRoute, defaults to /services/." @@ -194,6 +219,20 @@ spec: version: type: string description: "Protocol version (SHOULD per ERC-8004 spec)." + skills: + type: array + description: >- + OASF skills for discovery (e.g. natural_language_processing/text_generation). + Mapped to an OASF service entry in the registration JSON. + items: + type: string + domains: + type: array + description: >- + OASF domains for discovery (e.g. technology/artificial_intelligence). + Mapped to an OASF service entry in the registration JSON. + items: + type: string supportedTrust: type: array description: >- @@ -201,6 +240,14 @@ spec: Valid values: reputation, crypto-economic, tee-attestation. items: type: string + metadata: + type: object + description: >- + Additional registration metadata published into the generated + agent-registration.json for discovery and ranking (for example: + gpu, framework, best_val_bpb, total_experiments). + additionalProperties: + type: string status: type: object properties: diff --git a/internal/embed/skills/autoresearch-coordinator/SKILL.md b/internal/embed/skills/autoresearch-coordinator/SKILL.md new file mode 100644 index 00000000..152c2c2f --- /dev/null +++ b/internal/embed/skills/autoresearch-coordinator/SKILL.md @@ -0,0 +1,180 @@ +--- +name: autoresearch-coordinator +description: "Coordinate distributed autoresearch experiments across GPU workers discovered via ERC-8004 and paid via x402 micropayments." +metadata: { "openclaw": { "emoji": "\ud83d\udd2c", "requires": { "bins": ["python3", "curl"] } } } +--- + +# Autoresearch Coordinator + +Coordinate distributed autoresearch experiments across GPU workers discovered on-chain via ERC-8004 and paid per-experiment via x402 micropayments. This replaces the Ensue-based shared-memory coordinator from autoresearch-at-home with a fully decentralised discovery and payment loop built on obol-stack primitives. + +## When to Use + +- Discovering GPU workers advertising `devops_mlops/model_versioning` capabilities via the preferred public index API (internal Reth indexer first, then 8004scan fallback) +- Probing worker endpoints for x402 pricing before submitting experiments +- Submitting `train.py` experiments to remote GPU workers through x402 payment gates +- Running the continuous THINK/CLAIM/RUN/PUBLISH experiment loop +- Viewing the global leaderboard of autoresearch results from worker metadata +- Coordinating multi-worker experiment campaigns + +## When NOT to Use + +- Selling your own GPU as a worker -- use `autoresearch-worker` (then monetize it with `obol sell http`) +- Buying generic inference (chat completions) -- use `buy-inference` +- Discovering agents without running experiments -- use `discovery` +- Signing transactions directly -- use `ethereum-local-wallet` +- Cluster diagnostics -- use `obol-stack` + +## Quick Start + +```bash +# Discover available GPU workers from the preferred public index API +python3 scripts/coordinate.py discover + +# Discover with custom limit +python3 scripts/coordinate.py discover --limit 5 + +# Probe a specific worker for pricing +python3 scripts/coordinate.py probe https://worker.example.com/services/autoresearch-worker + +# Submit a single experiment to a worker +python3 scripts/coordinate.py submit https://worker.example.com/services/autoresearch-worker train.py + +# Submit with custom config overrides +python3 scripts/coordinate.py submit https://worker.example.com/services/autoresearch-worker train.py \ + --config '{"batch_size": 64, "learning_rate": 0.001}' + +# View global leaderboard (best val_bpb across all workers) +python3 scripts/coordinate.py leaderboard + +# Run continuous experiment loop (discover -> pick -> submit -> publish) +python3 scripts/coordinate.py loop train.py + +# Loop with worker preference and max rounds +python3 scripts/coordinate.py loop train.py --prefer https://worker.example.com/services/autoresearch-worker --rounds 10 +``` + +## Commands + +| Command | Description | +|---------|-------------| +| `discover [--limit N]` | Query the preferred public index API for GPU workers with `devops_mlops/model_versioning` skill | +| `probe ` | Send unauthenticated request to parse 402 pricing from the worker | +| `submit [--config JSON]` | Submit experiment with x402 payment (pre-sign ERC-3009, attach X-PAYMENT) | +| `leaderboard [--limit N]` | Query 8004scan for all autoresearch workers, rank by best `val_bpb` | +| `loop [--prefer URL] [--rounds N]` | Continuous loop: discover, pick best worker, submit, collect, publish | + +## The Experiment Loop + +The coordinator implements a THINK/CLAIM/RUN/PUBLISH loop: + +1. **THINK** -- Read current `train.py`, review leaderboard results, decide on next experiment variation +2. **CLAIM** -- Discover workers via 8004scan, probe pricing, select best worker (cheapest or preferred) +3. **RUN** -- Submit `train.py` + config to worker via POST `/experiment` through the x402 payment gate +4. **PUBLISH** -- Record result locally with provenance metadata (val_bpb, experiment_id, train.py hash, worker endpoint) + +Each step is atomic and idempotent. If a worker fails mid-experiment, the coordinator retries with the next available worker. + +## How Discovery Works + +Workers register on-chain via ERC-8004 and advertise capabilities through OASF (Open Agent Skills Framework) metadata. The coordinator prefers an internal Reth-backed indexer when `OBOL_INDEXER_API_URL` is healthy and otherwise falls back to the public 8004scan API: + +``` +GET https://www.8004scan.io/api/v1/public/agents + ?protocol=OASF + &search=devops_mlops/model_versioning + &limit=20 +``` + +The API returns agent summary objects. The coordinator then prefers the embedded registration document in `raw_metadata.offchain_content` and falls back to the off-chain registration URI when needed. + +From the registration document it extracts: +- service endpoints (where to POST experiments) +- x402 support flag (payment-gated access) +- OASF capability metadata from the `services[]` entry with `name: OASF` +- leaderboard metadata such as `best_val_bpb` + +## How Payment Works + +Experiment submission uses the same x402 payment flow as `buy-inference`: + +1. **Probe** -- Send unauthenticated POST to worker endpoint, receive `402 Payment Required` with pricing +2. **Sign** -- Pre-sign an ERC-3009 `TransferWithAuthorization` voucher via the current remote-signer API (`GET /api/v1/keys`, `POST /api/v1/sign/
/typed-data`) +3. **Submit** -- Re-send the POST with the `X-PAYMENT` header containing the signed voucher +4. **Settle** -- Worker's x402 verifier validates payment via the facilitator, forwards request to GPU + +Payment is per-experiment (not per-token). The 402 response includes `maxAmountRequired` which is the cost for one experiment run. + +## How Results are Published + +After collecting a result from a worker, the coordinator stores provenance metadata locally: + +```json +{ + "experiment_id": "exp-20260312-a1b2c3", + "train_hash": "sha256:abcdef...", + "val_bpb": 1.234, + "worker_endpoint": "https://worker.example.com/services/autoresearch", + "worker_agent_id": 42, + "timestamp": "2026-03-12T10:30:00Z", + "payment_tx": "0xdeadbeef..." +} +``` + +Results are appended to `$DATA_DIR/autoresearch/results.jsonl` (one JSON object per line). + +## Environment Variables + +| Variable | Default | Description | +|----------|---------|-------------| +| `OBOL_INDEXER_API_URL` | `` | Preferred internal index API base URL. Accepts either the service root or `/api/v1/public`; `/health` must report ready before it will be used | +| `SCAN_API_URL` | `https://www.8004scan.io/api/v1/public` | Fallback public API base URL used when the preferred internal indexer is unavailable or unhealthy | +| `REMOTE_SIGNER_URL` | `http://remote-signer:9000` | Remote-signer REST API for payment signing | +| `ERPC_URL` | `http://erpc.erpc.svc.cluster.local:4000/rpc` | eRPC gateway base URL | +| `ERPC_NETWORK` | `base-sepolia` | Default chain for payment | +| `DATA_DIR` | `/data` | Base directory for result storage | + +## Architecture + +``` +coordinate.py + | + +-- discover: 8004scan API (OASF filter) + | | + | v + | Worker .well-known/agent-registration.json + | + +-- probe: POST /experiment (no payment) + | | + | v + | 402 Payment Required (pricing JSON) + | + +-- submit: ERC-3009 sign via remote-signer + | | + | +-- POST /experiment + X-PAYMENT header + | | | + | | v + | | x402 verifier -> facilitator -> GPU worker + | | | + | | v + | | 200 + experiment result + | | + | v + +-- publish: results.jsonl (local provenance) +``` + +## Constraints + +- **Requires remote-signer** -- must have agent wallet provisioned via `obol openclaw onboard` +- **Requires network access** -- 8004scan API and worker endpoints must be reachable +- **Python stdlib only** -- uses `urllib`; no third-party Python dependencies required +- **Per-experiment payment** -- each submission costs one x402 payment; monitor balance via `buy-inference balance` +- **Worker availability** -- workers may go offline between discovery and submission; coordinator retries automatically +- **Result storage is local** -- provenance metadata stored on-disk, not on-chain (future: IPFS/on-chain attestations) + +## References + +- `references/coordination-protocol.md` -- Ensue-to-obol mapping, discovery flow, payment flow, leaderboard format +- See also: `discovery` skill for raw ERC-8004 registry queries +- See also: `buy-inference` skill for the x402 buyer sidecar architecture +- See also: `sell` skill for running a GPU worker (sell-side) diff --git a/internal/embed/skills/autoresearch-coordinator/references/coordination-protocol.md b/internal/embed/skills/autoresearch-coordinator/references/coordination-protocol.md new file mode 100644 index 00000000..9400ce87 --- /dev/null +++ b/internal/embed/skills/autoresearch-coordinator/references/coordination-protocol.md @@ -0,0 +1,192 @@ +# Coordination Protocol Reference + +How the autoresearch coordinator maps from the original Ensue-based shared-memory model to obol-stack's decentralised primitives. + +## Ensue to obol-stack Mapping + +| Ensue Concept | obol-stack Equivalent | Notes | +|---|---|---| +| Shared memory (Redis/filesystem) | ERC-8004 on-chain registry + 8004scan API | Workers register capabilities on-chain; coordinator discovers via API | +| Task queue (Ensue scheduler) | Direct HTTP POST to worker `/experiment` endpoint | No central queue; coordinator submits directly to chosen worker | +| Worker discovery (static config) | 8004scan OASF query (`devops_mlops/model_versioning`) | Dynamic discovery; workers join/leave without coordinator restart | +| Payment (none / trust-based) | x402 micropayments (USDC via ERC-3009 pre-signed auths) | Per-experiment payment; no credit accounts or invoicing | +| Result aggregation (shared DB) | Local `results.jsonl` + worker `.well-known` metadata | Coordinator stores locally; workers publish best scores in registration | +| Leaderboard (centralized) | 8004scan metadata aggregation | Workers self-report best `val_bpb` in their registration JSON | +| Worker health checks (heartbeat) | x402 probe (402 = alive, timeout = dead) | Payment gate doubles as health check | +| Experiment versioning (git) | SHA-256 hash of `train.py` (`train_hash` field) | Immutable reference to exact code submitted | + +## Discovery Flow + +``` +Coordinator 8004scan Worker + | | | + |-- GET /api/v1/public/agents ------>| | + | ?protocol=OASF | | + | &search=machine_learning/ | | + | model_optimization | | + | &limit=20 | | + | | | + |<-- {data: [agent summaries]} ------| | + | | | + | (prefer raw_metadata.offchain_content when present) | + | | | + |-- GET (fallback only) ---------------------------->| + | | + |<-- {services: [...], x402Support: true, metadata: {...}} ---------| + | | + | (extract endpoint, verify x402 + OASF service entry) | +``` + +### 8004scan API Parameters + +| Parameter | Type | Description | +|---|---|---| +| `protocol` | string | Filter by protocol: `OASF`, `MCP`, `A2A`, `Web`, `Email` | +| `search` | string | Keyword search across name, description, skills | +| `chainId` | int | Filter by chain (e.g., 84532 for Base Sepolia) | +| `ownerAddress` | address | Filter by registration owner | +| `sortBy` | string | Sort field (e.g., `registeredAt`) | +| `limit` | int | Max results to return | + +### Worker Registration JSON + +Workers advertise capabilities in their `.well-known/agent-registration.json`: + +```json +{ + "type": "https://eips.ethereum.org/EIPS/eip-8004#registration-v1", + "name": "GPU Worker Alpha", + "description": "A100 GPU worker for autoresearch experiments", + "services": [ + { + "name": "web", + "endpoint": "https://worker.example.com/services/autoresearch-worker", + "version": "1.0.0" + }, + { + "name": "OASF", + "version": "0.8", + "skills": ["devops_mlops/model_versioning"], + "domains": ["research_and_development/scientific_research"] + } + ], + "x402Support": true, + "metadata": { + "gpu": "A100-80GB", + "framework": "pytorch", + "best_val_bpb": "1.234", + "total_experiments": "42", + "updated": "2026-03-12T10:30:00Z" + }, + "active": true +} +``` + +## Payment Flow + +``` +Coordinator Worker (x402 gate) Facilitator Chain + | | | | + |-- POST /experiment --------->| | | + | (no X-PAYMENT header) | | | + | | | | + |<-- 402 Payment Required -----| | | + | {payTo, network, | | | + | maxAmountRequired} | | | + | | | | + |-- sign ERC-3009 auth --------|---------------------------|----------------->| + | (GET /api/v1/keys + | | | + | POST /api/v1/sign//typed-data) | | + | | | | + |-- POST /experiment --------->| | | + | X-PAYMENT: {signature, | | | + | authorization, chain, |-- verify payment -------->| | + | token} | |-- settle USDC -->| + | body: {train_py, config} |<-- 200 OK (valid) -------| | + | | | | + | |-- run experiment | | + | | (GPU training) | | + | | | | + |<-- 200 {val_bpb, metrics} ---| | | +``` + +### ERC-3009 Authorization Structure + +Each payment authorization contains: + +| Field | Type | Description | +|---|---|---| +| `from` | address | Coordinator's wallet (agent wallet from remote-signer) | +| `to` | address | Worker's `payTo` address (from 402 response) | +| `value` | uint256 | Payment amount in USDC micro-units | +| `validAfter` | uint256 | Unix timestamp (0 = immediately valid) | +| `validBefore` | uint256 | Unix timestamp (current time + 1 hour) | +| `nonce` | bytes32 | Random 32-byte nonce (single-use, prevents replay) | + +### X-PAYMENT Header Format + +```json +{ + "signature": "0x...", + "authorization": { + "from": "0xCoordinatorWallet", + "to": "0xWorkerPayTo", + "value": "1000", + "validAfter": "0", + "validBefore": "1741784400", + "nonce": "0xrandom32bytes..." + }, + "chain": "base-sepolia", + "token": "0x036CbD53842c5426634e7929541eC2318f3dCF7e" +} +``` + +## Leaderboard Metadata Format + +Workers publish their best results in the `.well-known/agent-registration.json` metadata section. The coordinator aggregates these via 8004scan queries. + +### Required Fields + +| Field | Type | Description | +|---|---|---| +| `metadata.best_val_bpb` | string | Best validation bits-per-byte achieved (stringified float) | +| `metadata.total_experiments` | string | Total experiments processed by this worker (stringified int) | +| `metadata.updated` | string | ISO 8601 timestamp of last result update | + +### Optional Fields + +| Field | Type | Description | +|---|---|---| +| `metadata.gpu` | string | GPU model (e.g., `A100-80GB`, `H100`) | +| `metadata.framework` | string | Training framework (e.g., `pytorch`, `jax`) | +| `metadata.best_experiment_hash` | string | SHA-256 hash of the train.py that produced the best result | +| `metadata.avg_experiment_time` | float | Average seconds per experiment | + +### Leaderboard Ranking + +The coordinator ranks workers by `metadata.best_val_bpb` in ascending order (lower is better). When querying the leaderboard: + +1. Fetch all workers with `devops_mlops/model_versioning` skill from 8004scan +2. For each worker, fetch their registration JSON +3. Extract `metadata.best_val_bpb` (skip workers without this field) +4. Sort ascending by `val_bpb` +5. Display rank, score, agent name, and last update time + +### Local Results Format + +The coordinator also maintains a local `results.jsonl` file for provenance tracking. Each line is a JSON object: + +```json +{ + "experiment_id": "exp-20260312-a1b2c3", + "train_hash": "sha256:abcdef1234567890...", + "val_bpb": 1.234, + "worker_endpoint": "https://worker.example.com/services/autoresearch", + "worker_agent_id": 42, + "timestamp": "2026-03-12T10:30:00Z", + "chain": "base-sepolia", + "raw_result": { "...worker response..." } +} +``` + +The `experiment_id` format is `exp-YYYYMMDD-XXXXXX` where `XXXXXX` is 6 random hex chars. The `train_hash` is the SHA-256 of the exact `train.py` source submitted, providing an immutable reference for reproducibility. diff --git a/internal/embed/skills/autoresearch-coordinator/scripts/coordinate.py b/internal/embed/skills/autoresearch-coordinator/scripts/coordinate.py new file mode 100644 index 00000000..c7c0fb0f --- /dev/null +++ b/internal/embed/skills/autoresearch-coordinator/scripts/coordinate.py @@ -0,0 +1,1046 @@ +#!/usr/bin/env python3 +"""coordinate.py -- Distributed autoresearch coordinator via obol-stack. + +Discovers GPU workers registered on ERC-8004 via the 8004scan API, probes +their x402 pricing, submits experiments with micropayments, and tracks +results with local provenance metadata. + +Replaces the Ensue-based shared-memory coordinator from autoresearch-at-home +with decentralised discovery (ERC-8004) and payment (x402). + +Usage: + python3 coordinate.py [args] + +Commands: + discover [--limit N] List GPU workers from 8004scan + probe Check x402 pricing for a worker + submit [--config JSON] Submit experiment with payment + leaderboard [--limit N] Global rankings by val_bpb + loop [--prefer URL] [--rounds N] Continuous experiment loop +""" + +import argparse +import base64 +import hashlib +import json +import os +import sys +import time +import urllib.error +import urllib.parse +import urllib.request +from datetime import datetime, timezone + +# --------------------------------------------------------------------------- +# Import shared helpers from sibling skills +# --------------------------------------------------------------------------- + +SKILL_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +SIGNER_SCRIPTS = os.path.join(os.path.dirname(SKILL_DIR), "ethereum-local-wallet", "scripts") +sys.path.insert(0, SIGNER_SCRIPTS) + +try: + from signer import _signer_get, _signer_post # noqa: E402 + HAS_SIGNER = True +except ImportError: + HAS_SIGNER = False + +# --------------------------------------------------------------------------- +# Constants +# --------------------------------------------------------------------------- + +SCAN_API_URL = os.environ.get( + "SCAN_API_URL", "https://www.8004scan.io/api/v1/public" +) +REMOTE_SIGNER_URL = os.environ.get("REMOTE_SIGNER_URL", "http://remote-signer:9000") +ERPC_URL = os.environ.get("ERPC_URL", "http://erpc.erpc.svc.cluster.local:4000/rpc") +DEFAULT_CHAIN = os.environ.get("ERPC_NETWORK", "base-sepolia") +DATA_DIR = os.environ.get("DATA_DIR", "/data") + +RESULTS_DIR = os.path.join(DATA_DIR, "autoresearch") +RESULTS_FILE = os.path.join(RESULTS_DIR, "results.jsonl") + +OASF_SKILL_FILTER = "devops_mlops/model_versioning" + +CHAIN_IDS = { + "base-sepolia": 84532, + "base": 8453, + "ethereum": 1, + "mainnet": 1, + "sepolia": 11155111, +} + +USDC_CONTRACTS = { + "base-sepolia": "0x036CbD53842c5426634e7929541eC2318f3dCF7e", + "base": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + "ethereum": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", +} + +USDC_DOMAIN_NAME = "USDC" +USDC_DOMAIN_VERSION = "2" + +DEFAULT_LOOP_DELAY = 60 # seconds between loop iterations + + +# --------------------------------------------------------------------------- +# HTTP helpers +# --------------------------------------------------------------------------- + +def _http_get(url, headers=None, timeout=30): + """GET request returning parsed JSON.""" + hdrs = {"Accept": "application/json", "User-Agent": "obol-autoresearch/1.0"} + if headers: + hdrs.update(headers) + req = urllib.request.Request(url, headers=hdrs, method="GET") + with urllib.request.urlopen(req, timeout=timeout) as resp: + return json.loads(resp.read()) + + +def _http_post(url, body, headers=None, timeout=120): + """POST request returning (status_code, headers_dict, body_bytes).""" + hdrs = {"Content-Type": "application/json", "User-Agent": "obol-autoresearch/1.0"} + if headers: + hdrs.update(headers) + data = json.dumps(body).encode() if isinstance(body, dict) else body + req = urllib.request.Request(url, data=data, headers=hdrs, method="POST") + try: + with urllib.request.urlopen(req, timeout=timeout) as resp: + return resp.status, dict(resp.headers), resp.read() + except urllib.error.HTTPError as e: + with e: + return e.code, dict(e.headers), e.read() + + +# --------------------------------------------------------------------------- +# 8004scan API +# --------------------------------------------------------------------------- + +def build_scan_api_url(protocol="OASF", search=None, limit=20, chain_id=None, + sort_by=None, owner_address=None): + """Build the 8004scan /agents query URL.""" + params = {"limit": limit} + if protocol: + params["protocol"] = protocol + if search: + params["search"] = search + if chain_id: + params["chainId"] = chain_id + if sort_by: + params["sortBy"] = sort_by + if owner_address: + params["ownerAddress"] = owner_address + base = SCAN_API_URL.rstrip("/") + return f"{base}/agents?{urllib.parse.urlencode(params)}" + + +def query_8004scan(protocol="OASF", search=None, limit=20, chain_id=None, + sort_by=None, owner_address=None): + """Query the 8004scan public API for registered agents. + + Supports filtering by protocol (MCP/A2A/OASF/Web/Email), keyword search, + chainId, ownerAddress, and sorting. + + Returns list of agent summary objects. + """ + url = build_scan_api_url(protocol, search, limit, chain_id, sort_by, owner_address) + try: + result = _http_get(url) + if isinstance(result, dict): + data = result.get("data", result.get("items", [])) + if isinstance(data, list): + if not data and search: + # Fall back to protocol-only listing if the keyword search is too strict. + fallback = _http_get(build_scan_api_url(protocol, None, limit, chain_id, sort_by, owner_address)) + if isinstance(fallback, dict): + fallback_data = fallback.get("data", fallback.get("items", [])) + if isinstance(fallback_data, list): + return fallback_data + elif isinstance(fallback, list): + return fallback + return data + if isinstance(result, list): + return result + return [] + except (urllib.error.URLError, urllib.error.HTTPError, json.JSONDecodeError) as e: + print(f"Error querying 8004scan: {e}", file=sys.stderr) + return [] + + +def fetch_registration_json(uri): + """Fetch the .well-known/agent-registration.json from a worker's URI.""" + if not uri: + return None + if isinstance(uri, dict): + return uri + if uri.startswith("data:application/json;base64,"): + try: + raw = uri.split(",", 1)[1] + return json.loads(base64.b64decode(raw).decode("utf-8")) + except (ValueError, json.JSONDecodeError) as e: + print(f" Warning: Failed to decode registration data URI: {e}", file=sys.stderr) + return None + if not (uri.startswith("http://") or uri.startswith("https://")): + return None + try: + return _http_get(uri, timeout=15) + except (urllib.error.URLError, urllib.error.HTTPError, json.JSONDecodeError) as e: + print(f" Warning: Failed to fetch {uri}: {e}", file=sys.stderr) + return None + + +def extract_registration_from_agent(agent): + """Extract the registration document from a current 8004scan agent summary.""" + raw = agent.get("raw_metadata", {}) if isinstance(agent, dict) else {} + offchain = raw.get("offchain_content") if isinstance(raw, dict) else None + if isinstance(offchain, dict): + return offchain + uri = ( + agent.get("uri") + or agent.get("tokenURI") + or (raw.get("offchain_uri") if isinstance(raw, dict) else None) + or agent.get("agent_url") + ) + return fetch_registration_json(uri) + + +def extract_worker_endpoint(registration): + """Extract the experiment submission endpoint from a registration JSON. + + Looks for a service with x402Support and an endpoint path containing + '/services/' or '/experiment'. + """ + if not registration: + return None + + services = registration.get("services", []) + for svc in services: + endpoint = svc.get("endpoint", "") + if "/services/" in endpoint or "/experiment" in endpoint: + return endpoint + + # Fallback: if x402Support is true, try constructing from the first service + if registration.get("x402Support") and services: + return services[0].get("endpoint") + + return None + + +def extract_oasf_skills(registration): + """Extract OASF skills/domains from registration metadata.""" + found = [] + + services = registration.get("services", []) if isinstance(registration, dict) else [] + if isinstance(services, list): + for svc in services: + if not isinstance(svc, dict): + continue + if str(svc.get("name", "")).upper() != "OASF": + continue + for key in ("skills", "domains"): + value = svc.get(key, []) + if isinstance(value, list): + for item in value: + if item is not None: + found.append(str(item)) + + if found: + return found + + oasf = registration.get("oasf", registration.get("skills", [])) if isinstance(registration, dict) else [] + if isinstance(oasf, list): + for s in oasf: + if isinstance(s, dict): + for key in ("skills", "domains"): + value = s.get(key, []) + if isinstance(value, list): + for item in value: + if item is not None: + found.append(str(item)) + domain = s.get("domain") + if domain is not None: + found.append(str(domain)) + name = s.get("name") + if name is not None: + found.append(str(name)) + elif isinstance(s, str): + found.append(s) + return found + + +# --------------------------------------------------------------------------- +# x402 payment helpers +# --------------------------------------------------------------------------- + +def parse_402_pricing(headers, body): + """Parse pricing info from a 402 Payment Required response. + + Returns dict with: payTo, network, maxAmountRequired, facilitatorURL, + or None if unparseable. + """ + try: + data = json.loads(body) if isinstance(body, bytes) else body + except (json.JSONDecodeError, TypeError): + data = {} + + # x402 pricing can be in response body or headers + pricing = {} + + # x402 V1 standard format: pricing in accepts[] array + accepts = data.get("accepts", []) + if accepts and isinstance(accepts, list): + offer = accepts[0] + for key in ("payTo", "network", "maxAmountRequired", "asset", + "scheme", "description", "maxTimeoutSeconds"): + if key in offer: + pricing[key] = offer[key] + + # Try body fields (flat format) + for key in ("payTo", "network", "maxAmountRequired", "facilitatorURL", + "price", "priceModel", "description"): + if key in data: + pricing[key] = data[key] + + # Try x402-specific headers + if "X-Payment-PayTo" in headers: + pricing["payTo"] = headers["X-Payment-PayTo"] + if "X-Payment-Network" in headers: + pricing["network"] = headers["X-Payment-Network"] + if "X-Payment-Amount" in headers: + pricing["maxAmountRequired"] = headers["X-Payment-Amount"] + + # Also check for nested pricing structure + if "pricing" in data and isinstance(data["pricing"], dict): + pricing.update(data["pricing"]) + + if not pricing.get("payTo") and not pricing.get("maxAmountRequired"): + return None + + return pricing + + +def sign_erc3009_auth(pay_to, amount, chain=None): + """Sign an ERC-3009 TransferWithAuthorization via the remote-signer. + + Returns the signed authorization dict suitable for an X-PAYMENT header, + or None on failure. + """ + if not HAS_SIGNER: + print("Error: remote-signer helpers not available", file=sys.stderr) + return None + + network = chain or DEFAULT_CHAIN + chain_id = CHAIN_IDS.get(network) + usdc = USDC_CONTRACTS.get(network) + if not chain_id or not usdc: + print(f"Error: unsupported chain '{network}' for payment", file=sys.stderr) + return None + + # Get signer address using the same API as ethereum-local-wallet. + try: + info = _signer_get("/api/v1/keys") + if isinstance(info, dict): + keys = info.get("keys", []) + signer_address = keys[0] if keys else None + elif isinstance(info, list): + signer_address = info[0] if info else None + else: + signer_address = None + if not signer_address: + print("Error: no keys in remote-signer", file=sys.stderr) + return None + except Exception as e: + print(f"Error contacting remote-signer: {e}", file=sys.stderr) + return None + + # Generate random nonce (32 bytes) + nonce = "0x" + os.urandom(32).hex() + + # Valid for 1 hour + valid_after = 0 + valid_before = int(time.time()) + 3600 + + # EIP-712 typed data for TransferWithAuthorization + typed_data = { + "types": { + "EIP712Domain": [ + {"name": "name", "type": "string"}, + {"name": "version", "type": "string"}, + {"name": "chainId", "type": "uint256"}, + {"name": "verifyingContract", "type": "address"}, + ], + "TransferWithAuthorization": [ + {"name": "from", "type": "address"}, + {"name": "to", "type": "address"}, + {"name": "value", "type": "uint256"}, + {"name": "validAfter", "type": "uint256"}, + {"name": "validBefore", "type": "uint256"}, + {"name": "nonce", "type": "bytes32"}, + ], + }, + "primaryType": "TransferWithAuthorization", + "domain": { + "name": USDC_DOMAIN_NAME, + "version": USDC_DOMAIN_VERSION, + "chainId": chain_id, + "verifyingContract": usdc, + }, + "message": { + "from": signer_address, + "to": pay_to, + "value": str(amount), + "validAfter": str(valid_after), + "validBefore": str(valid_before), + "nonce": nonce, + }, + } + + try: + sig_data = _signer_post(f"/api/v1/sign/{signer_address}/typed-data", typed_data) + signature = sig_data.get("signature") if isinstance(sig_data, dict) else sig_data + if not signature: + print("Error: remote-signer returned empty signature", file=sys.stderr) + return None + return { + "signature": signature, + "authorization": typed_data["message"], + "chain": network, + "token": usdc, + } + except Exception as e: + print(f"Error signing authorization: {e}", file=sys.stderr) + return None + + +def build_x_payment_header(signed_auth): + """Encode a signed authorization as an X-PAYMENT header value (JSON).""" + if not signed_auth: + return None + return json.dumps(signed_auth) + + +# --------------------------------------------------------------------------- +# Result provenance +# --------------------------------------------------------------------------- + +def _ensure_results_dir(): + """Create the results directory if it does not exist.""" + os.makedirs(RESULTS_DIR, exist_ok=True) + + +def save_result(result): + """Append an experiment result to the local results.jsonl.""" + _ensure_results_dir() + with open(RESULTS_FILE, "a") as f: + f.write(json.dumps(result) + "\n") + + +def load_results(): + """Load all experiment results from results.jsonl.""" + if not os.path.exists(RESULTS_FILE): + return [] + results = [] + with open(RESULTS_FILE, "r") as f: + for line in f: + line = line.strip() + if line: + try: + results.append(json.loads(line)) + except json.JSONDecodeError: + continue + return results + + +def compute_train_hash(train_py_path): + """Compute SHA-256 hash of a train.py file.""" + h = hashlib.sha256() + with open(train_py_path, "rb") as f: + for chunk in iter(lambda: f.read(8192), b""): + h.update(chunk) + return f"sha256:{h.hexdigest()}" + + +def generate_experiment_id(): + """Generate a unique experiment ID.""" + ts = datetime.now(timezone.utc).strftime("%Y%m%d") + suffix = os.urandom(3).hex() + return f"exp-{ts}-{suffix}" + + +# --------------------------------------------------------------------------- +# ObolCoordinator +# --------------------------------------------------------------------------- + +class ObolCoordinator: + """Coordinates distributed autoresearch experiments via obol-stack. + + Discovers GPU workers through 8004scan, pays per-experiment via x402, + and tracks results with local provenance metadata. + """ + + def __init__(self, chain=None): + self.chain = chain or DEFAULT_CHAIN + + def discover_workers(self, limit=20): + """Query 8004scan for workers advertising devops_mlops/model_versioning. + + Returns list of dicts with keys: name, endpoint, uri, agent_id, skills, x402. + """ + agents = query_8004scan( + protocol="OASF", + search=OASF_SKILL_FILTER, + limit=limit, + ) + + workers = [] + for agent in agents: + raw = agent.get("raw_metadata", {}) if isinstance(agent, dict) else {} + uri = agent.get("uri", agent.get("tokenURI", "")) or (raw.get("offchain_uri", "") if isinstance(raw, dict) else "") + agent_id = agent.get("agentId") or agent.get("agent_id") or agent.get("id") or agent.get("token_id") + name = agent.get("name", f"agent-{agent_id or '?'}") + + registration = extract_registration_from_agent(agent) + endpoint = extract_worker_endpoint(registration) if registration else None + skills = extract_oasf_skills(registration) if registration else [] + x402 = bool((registration or {}).get("x402Support", agent.get("x402_supported", False))) + + workers.append({ + "name": name, + "endpoint": endpoint, + "uri": uri, + "agent_id": agent_id, + "skills": skills, + "x402": x402, + "registration": registration, + }) + + return workers + + def probe_worker(self, endpoint): + """Send unauthenticated request to worker, parse 402 for pricing. + + Returns pricing dict or None if the worker is not x402-gated. + """ + # Send a minimal experiment probe (empty body triggers 402 before processing) + probe_body = {"probe": True} + status, headers, body = _http_post( + endpoint.rstrip("/") + "/experiment", + probe_body, + timeout=30, + ) + + if status == 402: + pricing = parse_402_pricing(headers, body) + if pricing: + pricing["status"] = 402 + pricing["endpoint"] = endpoint + return pricing + print(f" Got 402 but could not parse pricing from {endpoint}", file=sys.stderr) + return None + + if 200 <= status < 300: + print(f" Worker at {endpoint} returned {status} (no payment gate)") + return {"status": status, "endpoint": endpoint, "free": True} + + print(f" Worker at {endpoint} returned unexpected status {status}", file=sys.stderr) + return None + + def submit_experiment(self, endpoint, train_py_source, config=None): + """Submit an experiment to a worker with x402 payment. + + Args: + endpoint: Worker's base endpoint URL + train_py_source: Contents of train.py as a string + config: Optional dict of config overrides for the experiment + + Returns result dict from the worker, or None on failure. + """ + experiment_url = endpoint.rstrip("/") + "/experiment" + + # Step 1: Probe for pricing + probe_body = {"probe": True} + status, headers, body = _http_post(experiment_url, probe_body, timeout=30) + + if status != 402: + if 200 <= status < 300: + # No payment gate -- submit directly + print(" Worker has no payment gate, submitting directly...") + return self._submit_direct(experiment_url, train_py_source, config) + print(f" Probe failed with status {status}", file=sys.stderr) + return None + + # Step 2: Parse pricing + pricing = parse_402_pricing(headers, body) + if not pricing: + print(" Could not parse 402 pricing", file=sys.stderr) + return None + + pay_to = pricing.get("payTo") + amount = pricing.get("maxAmountRequired", pricing.get("price")) + if not pay_to or not amount: + print(" Pricing missing payTo or amount", file=sys.stderr) + return None + + try: + amount_int = int(amount) + except (ValueError, TypeError): + print(f" Non-integer amount '{amount}', cannot sign payment", file=sys.stderr) + return None + + print(f" Price: {amount_int} USDC micro-units to {pay_to}") + + # Step 3: Sign ERC-3009 authorization + signed_auth = sign_erc3009_auth(pay_to, amount_int, self.chain) + if not signed_auth: + print(" Failed to sign payment authorization", file=sys.stderr) + return None + + # Step 4: Submit with payment + payment_header = build_x_payment_header(signed_auth) + submit_body = { + "train_py": train_py_source, + "config": config or {}, + } + submit_headers = {"X-PAYMENT": payment_header} + + print(" Submitting experiment with payment...") + status2, headers2, body2 = _http_post( + experiment_url, submit_body, headers=submit_headers, timeout=600 + ) + + if 200 <= status2 < 300: + try: + return json.loads(body2) + except json.JSONDecodeError: + return {"raw": body2.decode("utf-8", errors="replace"), "status": status2} + + print(f" Submission failed with status {status2}", file=sys.stderr) + try: + err = json.loads(body2) + print(f" Response: {json.dumps(err, indent=2)}", file=sys.stderr) + except (json.JSONDecodeError, TypeError): + print(f" Response: {body2[:500]}", file=sys.stderr) + return None + + def _submit_direct(self, url, train_py_source, config=None): + """Submit experiment without payment (free worker).""" + submit_body = { + "train_py": train_py_source, + "config": config or {}, + } + status, _, body = _http_post(url, submit_body, timeout=600) + if 200 <= status < 300: + try: + return json.loads(body) + except json.JSONDecodeError: + return {"raw": body.decode("utf-8", errors="replace"), "status": status} + return None + + def publish_result(self, val_bpb, experiment_id, train_hash, + worker_endpoint=None, worker_agent_id=None, extra=None): + """Store experiment result with provenance metadata locally.""" + result = { + "experiment_id": experiment_id, + "train_hash": train_hash, + "val_bpb": val_bpb, + "worker_endpoint": worker_endpoint, + "worker_agent_id": worker_agent_id, + "timestamp": datetime.now(timezone.utc).isoformat(), + "chain": self.chain, + } + if extra: + result.update(extra) + save_result(result) + return result + + def get_leaderboard(self, limit=20): + """Query 8004scan for autoresearch workers and rank by best val_bpb. + + Fetches worker registration metadata and extracts reported val_bpb + scores from their .well-known metadata. + """ + agents = query_8004scan( + protocol="OASF", + search=OASF_SKILL_FILTER, + limit=limit * 2, # fetch extra to account for workers without results + ) + + entries = [] + for agent in agents: + raw = agent.get("raw_metadata", {}) if isinstance(agent, dict) else {} + uri = agent.get("uri", agent.get("tokenURI", "")) or (raw.get("offchain_uri", "") if isinstance(raw, dict) else "") + agent_id = agent.get("agentId") or agent.get("agent_id") or agent.get("id") or agent.get("token_id") + name = agent.get("name", f"agent-{agent_id or '?'}") + registration = extract_registration_from_agent(agent) + if not registration: + continue + + # Look for autoresearch results in registration metadata. + meta = registration.get("metadata", registration.get("autoresearch", {})) + if isinstance(meta, dict): + val_bpb = meta.get("best_val_bpb", meta.get("val_bpb")) + if val_bpb is not None: + entries.append({ + "name": name, + "agent_id": agent_id, + "val_bpb": float(val_bpb), + "uri": uri, + "updated": meta.get("updated", agent.get("updated_at", "")), + }) + + # Sort by val_bpb ascending (lower is better) + entries.sort(key=lambda e: e["val_bpb"]) + return entries[:limit] + + def run_loop(self, train_py_path, prefer_endpoint=None, max_rounds=None): + """Run the continuous THINK/CLAIM/RUN/PUBLISH loop. + + Args: + train_py_path: Path to the train.py file + prefer_endpoint: Optional preferred worker endpoint + max_rounds: Max iterations (None = infinite) + """ + if not os.path.exists(train_py_path): + print(f"Error: {train_py_path} not found", file=sys.stderr) + return + + train_hash = compute_train_hash(train_py_path) + with open(train_py_path, "r") as f: + train_source = f.read() + + round_num = 0 + while max_rounds is None or round_num < max_rounds: + round_num += 1 + print(f"\n{'='*60}") + print(f"Round {round_num}") + print(f"{'='*60}") + + # THINK: Review current state + results = load_results() + best = min((r["val_bpb"] for r in results if "val_bpb" in r), default=None) + if best is not None: + print(f" Current best val_bpb: {best:.4f} ({len(results)} experiments)") + else: + print(" No previous results") + + # CLAIM: Discover and select worker + print("\n Discovering workers...") + if prefer_endpoint: + # Use preferred endpoint directly + print(f" Using preferred worker: {prefer_endpoint}") + endpoint = prefer_endpoint + else: + workers = self.discover_workers(limit=10) + available = [w for w in workers if w.get("endpoint") and w.get("x402")] + if not available: + print(" No available workers found. Waiting before retry...") + time.sleep(DEFAULT_LOOP_DELAY) + continue + # Pick first available (could be enhanced with pricing comparison) + worker = available[0] + endpoint = worker["endpoint"] + print(f" Selected worker: {worker['name']} at {endpoint}") + + # Probe pricing + print("\n Probing pricing...") + pricing = self.probe_worker(endpoint) + if not pricing: + print(" Could not get pricing. Skipping this round.") + time.sleep(DEFAULT_LOOP_DELAY) + continue + if not pricing.get("free"): + print(f" Cost: {pricing.get('maxAmountRequired', pricing.get('price', '?'))} USDC micro-units") + + # RUN: Submit experiment + print("\n Submitting experiment...") + experiment_id = generate_experiment_id() + result = self.submit_experiment(endpoint, train_source) + if not result: + print(" Experiment submission failed. Trying next round.") + time.sleep(DEFAULT_LOOP_DELAY) + continue + + # PUBLISH: Record result + val_bpb = result.get("val_bpb", result.get("metrics", {}).get("val_bpb")) + if val_bpb is not None: + published = self.publish_result( + val_bpb=float(val_bpb), + experiment_id=experiment_id, + train_hash=train_hash, + worker_endpoint=endpoint, + extra={"raw_result": result}, + ) + print(f"\n Result: val_bpb = {val_bpb:.4f}") + print(f" Saved as {experiment_id}") + if best is not None and float(val_bpb) < best: + print(f" NEW BEST! (improved from {best:.4f})") + else: + print(f"\n Experiment completed but no val_bpb in result:") + print(f" {json.dumps(result, indent=2)[:500]}") + # Still save for provenance + self.publish_result( + val_bpb=None, + experiment_id=experiment_id, + train_hash=train_hash, + worker_endpoint=endpoint, + extra={"raw_result": result, "note": "no val_bpb returned"}, + ) + + if max_rounds is not None and round_num >= max_rounds: + print(f"\n Completed {max_rounds} rounds.") + break + + print(f"\n Waiting {DEFAULT_LOOP_DELAY}s before next round...") + time.sleep(DEFAULT_LOOP_DELAY) + + +# --------------------------------------------------------------------------- +# CLI commands +# --------------------------------------------------------------------------- + +def cmd_discover(args): + """List available GPU workers from 8004scan.""" + limit = args.limit or 20 + print(f"Discovering GPU workers with OASF skill '{OASF_SKILL_FILTER}'...") + print(f" API: {SCAN_API_URL}") + print() + + coordinator = ObolCoordinator(chain=args.chain) + workers = coordinator.discover_workers(limit=limit) + + if not workers: + print("No workers found.") + return + + print(f"Found {len(workers)} worker(s):\n") + print(f"{'Name':30} {'Agent ID':>10} {'x402':>5} Endpoint") + print(f"{'-'*30} {'-'*10} {'-'*5} {'-'*50}") + + for w in workers: + x402_str = "yes" if w.get("x402") else "no" + endpoint = w.get("endpoint") or "(none)" + name = (w.get("name") or "?")[:30] + agent_id = w.get("agent_id", "?") + print(f"{name:30} {str(agent_id):>10} {x402_str:>5} {endpoint}") + + if w.get("skills"): + print(f"{'':30} {'':>10} {'':>5} Skills: {', '.join(w['skills'][:5])}") + + +def cmd_probe(args): + """Probe a worker endpoint for x402 pricing.""" + endpoint = args.endpoint.rstrip("/") + print(f"Probing {endpoint} ...") + print() + + coordinator = ObolCoordinator(chain=args.chain) + pricing = coordinator.probe_worker(endpoint) + + if not pricing: + print("Could not get pricing info from this endpoint.") + sys.exit(1) + + if pricing.get("free"): + print("This worker has no payment gate (free access).") + return + + print("x402 Pricing:") + print(f" Status: {pricing.get('status', '?')}") + print(f" Pay To: {pricing.get('payTo', '?')}") + print(f" Network: {pricing.get('network', '?')}") + print(f" Amount: {pricing.get('maxAmountRequired', pricing.get('price', '?'))} USDC micro-units") + if pricing.get("priceModel"): + print(f" Price Model: {pricing['priceModel']}") + if pricing.get("description"): + print(f" Description: {pricing['description']}") + if pricing.get("facilitatorURL"): + print(f" Facilitator: {pricing['facilitatorURL']}") + + +def cmd_submit(args): + """Submit a single experiment to a worker.""" + endpoint = args.endpoint.rstrip("/") + train_py_path = args.train_py + + if not os.path.exists(train_py_path): + print(f"Error: {train_py_path} not found", file=sys.stderr) + sys.exit(1) + + config = None + if args.config: + try: + config = json.loads(args.config) + except json.JSONDecodeError as e: + print(f"Error: invalid JSON config: {e}", file=sys.stderr) + sys.exit(1) + + with open(train_py_path, "r") as f: + train_source = f.read() + + train_hash = compute_train_hash(train_py_path) + experiment_id = generate_experiment_id() + + print(f"Submitting experiment to {endpoint}") + print(f" train.py: {train_py_path}") + print(f" train hash: {train_hash}") + print(f" experiment ID: {experiment_id}") + if config: + print(f" config: {json.dumps(config)}") + print() + + coordinator = ObolCoordinator(chain=args.chain) + result = coordinator.submit_experiment(endpoint, train_source, config) + + if not result: + print("Experiment submission failed.", file=sys.stderr) + sys.exit(1) + + val_bpb = result.get("val_bpb", result.get("metrics", {}).get("val_bpb")) + + # Publish provenance + coordinator.publish_result( + val_bpb=float(val_bpb) if val_bpb is not None else None, + experiment_id=experiment_id, + train_hash=train_hash, + worker_endpoint=endpoint, + extra={"raw_result": result}, + ) + + print("Experiment completed!") + print(f" Result: {json.dumps(result, indent=2)[:1000]}") + if val_bpb is not None: + print(f" val_bpb: {val_bpb}") + print(f" Saved to {RESULTS_FILE}") + + +def cmd_leaderboard(args): + """Show global leaderboard from 8004scan worker metadata.""" + limit = args.limit or 20 + print(f"Fetching global autoresearch leaderboard...") + print() + + coordinator = ObolCoordinator(chain=args.chain) + entries = coordinator.get_leaderboard(limit=limit) + + if not entries: + # Fall back to local results + print("No leaderboard data from 8004scan. Showing local results:\n") + results = load_results() + if not results: + print("No local results either.") + return + + # Group by worker, show best per worker + by_worker = {} + for r in results: + key = r.get("worker_endpoint", "local") + if r.get("val_bpb") is not None: + if key not in by_worker or r["val_bpb"] < by_worker[key]["val_bpb"]: + by_worker[key] = r + + sorted_workers = sorted(by_worker.values(), key=lambda r: r["val_bpb"]) + print(f"{'Rank':>5} {'val_bpb':>10} {'Experiment':20} Worker") + print(f"{'-'*5} {'-'*10} {'-'*20} {'-'*40}") + for i, r in enumerate(sorted_workers[:limit], 1): + print(f"{i:>5} {r['val_bpb']:>10.4f} {r.get('experiment_id', '?'):20} {r.get('worker_endpoint', '?')}") + return + + print(f"{'Rank':>5} {'val_bpb':>10} {'Agent ID':>10} {'Name':30} Updated") + print(f"{'-'*5} {'-'*10} {'-'*10} {'-'*30} {'-'*20}") + for i, e in enumerate(entries, 1): + name = (e.get("name") or "?")[:30] + print(f"{i:>5} {e['val_bpb']:>10.4f} {str(e.get('agent_id', '?')):>10} {name:30} {e.get('updated', '?')}") + + +def cmd_loop(args): + """Run the continuous experiment loop.""" + train_py_path = args.train_py + prefer = args.prefer + rounds = args.rounds + + if not os.path.exists(train_py_path): + print(f"Error: {train_py_path} not found", file=sys.stderr) + sys.exit(1) + + coordinator = ObolCoordinator(chain=args.chain) + print(f"Starting experiment loop") + print(f" train.py: {train_py_path}") + print(f" chain: {coordinator.chain}") + if prefer: + print(f" prefer: {prefer}") + if rounds: + print(f" rounds: {rounds}") + print() + + try: + coordinator.run_loop(train_py_path, prefer_endpoint=prefer, max_rounds=rounds) + except KeyboardInterrupt: + print("\n\nLoop interrupted by user.") + results = load_results() + if results: + best = min((r["val_bpb"] for r in results if r.get("val_bpb") is not None), default=None) + print(f"Total experiments: {len(results)}") + if best is not None: + print(f"Best val_bpb: {best:.4f}") + + +# --------------------------------------------------------------------------- +# Main +# --------------------------------------------------------------------------- + +def main(): + parser = argparse.ArgumentParser( + description="Coordinate distributed autoresearch experiments via obol-stack" + ) + parser.add_argument("--chain", default=None, help="Chain/network for payments (default: base-sepolia)") + sub = parser.add_subparsers(dest="command", help="Command to run") + + # discover + p_discover = sub.add_parser("discover", help="List available GPU workers") + p_discover.add_argument("--limit", type=int, default=20, help="Max results (default: 20)") + + # probe + p_probe = sub.add_parser("probe", help="Check x402 pricing for a worker") + p_probe.add_argument("endpoint", help="Worker endpoint URL") + + # submit + p_submit = sub.add_parser("submit", help="Submit experiment to a worker") + p_submit.add_argument("endpoint", help="Worker endpoint URL") + p_submit.add_argument("train_py", help="Path to train.py") + p_submit.add_argument("--config", default=None, help="JSON config overrides") + + # leaderboard + p_leader = sub.add_parser("leaderboard", help="Show global rankings") + p_leader.add_argument("--limit", type=int, default=20, help="Max results (default: 20)") + + # loop + p_loop = sub.add_parser("loop", help="Run continuous experiment loop") + p_loop.add_argument("train_py", help="Path to train.py") + p_loop.add_argument("--prefer", default=None, help="Preferred worker endpoint URL") + p_loop.add_argument("--rounds", type=int, default=None, help="Max rounds (default: infinite)") + + args = parser.parse_args() + + if not args.command: + parser.print_help() + sys.exit(1) + + commands = { + "discover": cmd_discover, + "probe": cmd_probe, + "submit": cmd_submit, + "leaderboard": cmd_leaderboard, + "loop": cmd_loop, + } + + try: + commands[args.command](args) + except KeyboardInterrupt: + print("\nInterrupted.", file=sys.stderr) + sys.exit(130) + except (urllib.error.URLError, urllib.error.HTTPError, OSError) as e: + print(f"Network error: {e}", file=sys.stderr) + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/internal/embed/skills/autoresearch-worker/SKILL.md b/internal/embed/skills/autoresearch-worker/SKILL.md new file mode 100644 index 00000000..218a09ea --- /dev/null +++ b/internal/embed/skills/autoresearch-worker/SKILL.md @@ -0,0 +1,123 @@ +--- +name: autoresearch-worker +description: "Run a GPU worker that accepts paid autoresearch experiments over HTTP and monetize it through obol sell http." +metadata: { "openclaw": { "emoji": "🖥️", "requires": { "bins": ["python3", "uv"] } } } +--- + +# Autoresearch Worker + +Run a single-GPU worker that accepts `train.py` experiments over HTTP, executes them one at a time, stores results on disk, and exposes a simple API that can be gated with x402 using `obol sell http`. + +## When to Use + +- Selling GPU-backed autoresearch experiment execution +- Exposing a remote `POST /experiment` endpoint for the autoresearch coordinator +- Running a worker on a Linux GPU host with `k3s` +- Testing the sell-side of the GPU marketplace locally before exposing it publicly + +## When NOT to Use + +- Coordinating experiments across many workers — use `autoresearch-coordinator` +- Publishing optimized checkpoints as inference — use `autoresearch` +- Selling a generic HTTP app — use `sell` + +## What the Worker Exposes + +- `GET /health` / `GET /healthz` — worker health +- `GET /status` — busy/idle plus current, last, and best result +- `GET /best` — best known result +- `GET /experiments/` — fetch a stored result +- `POST /experiment` — submit a `train.py` experiment + +The worker is intentionally simple: +- one experiment at a time +- one GPU worker process +- local disk for logs/results +- no distributed queue or scheduler inside the worker + +## Quick Start + +### 1. Start the worker API + +```bash +python3 scripts/worker_api.py serve \ + --repo /path/to/autoresearch \ + --data-dir /data \ + --host 0.0.0.0 \ + --port 8080 \ + --timeout 300 +``` + +The repo path should point at a prepared autoresearch workdir/repo with the dependencies already available via `uv`. + +### 2. Verify the worker locally + +```bash +curl -s http://127.0.0.1:8080/health | jq . +curl -s http://127.0.0.1:8080/status | jq . +``` + +### 3. Deploy the worker behind a Kubernetes Service + +For production GPU sellers, prefer `k3s` on the GPU host. The monetization path is cluster-based, so the worker should be reachable as a Kubernetes Service. + +### 4. Monetize it with x402 + +```bash +obol sell http autoresearch-worker \ + --namespace autoresearch \ + --upstream autoresearch-worker \ + --port 8080 \ + --health-path /health \ + --wallet 0xYourWalletAddress \ + --chain base-sepolia \ + --per-hour 0.50 \ + --path /services/autoresearch-worker \ + --register \ + --register-name "GPU Worker Alpha" \ + --register-description "A GPU worker for paid autoresearch experiments" \ + --register-skills devops_mlops/model_versioning \ + --register-domains research_and_development/scientific_research +``` + +This creates a `ServiceOffer` that: +- health-checks the worker +- creates a payment-gated public route +- optionally registers the worker on ERC-8004 for discovery + +## Request Format + +Submit an experiment with: + +```json +{ + "train_py": "print('hello world')", + "config": { + "batch_size": 64, + "learning_rate": 0.001 + } +} +``` + +If the request makes it through the x402 gate, the worker runs the experiment synchronously and returns a result JSON document. + +## Important Constraints + +- **Single-flight** — one experiment at a time; concurrent submissions return `409` +- **Arbitrary code execution** — submitted `train.py` is executed, so run this only on infrastructure dedicated to the worker +- **Local persistence** — results are stored under `$DATA_DIR/autoresearch-worker/results/` +- **Approx pricing** — `--per-hour` is converted into a request price using the current 5-minute experiment budget assumption +- **Cluster-based monetization** — `obol sell http` expects a Kubernetes Service, so `k3s` is the preferred deployment target for GPU sellers + +## Files + +- `scripts/worker_api.py` — HTTP worker service +- `references/worker-api.md` — endpoint and deployment details +- `Dockerfile.worker` (repo root) — container image for the worker + +## References + +- `references/worker-api.md` — worker endpoints, result schema, and deployment notes +- `references/k3s-gpu-worker.md` — minimal k3s Deployment + Service example +- See also: `sell` for ServiceOffer monetization +- See also: `autoresearch-coordinator` for the buyer/coordinator side of remote experiments diff --git a/internal/embed/skills/autoresearch-worker/references/k3s-gpu-worker.md b/internal/embed/skills/autoresearch-worker/references/k3s-gpu-worker.md new file mode 100644 index 00000000..32ac7397 --- /dev/null +++ b/internal/embed/skills/autoresearch-worker/references/k3s-gpu-worker.md @@ -0,0 +1,76 @@ +# k3s GPU Worker Deployment Example + +This example shows the minimal shape for running the autoresearch worker inside a `k3s` cluster on a GPU host. + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: autoresearch-worker + namespace: autoresearch +spec: + replicas: 1 + selector: + matchLabels: + app: autoresearch-worker + template: + metadata: + labels: + app: autoresearch-worker + spec: + containers: + - name: worker + image: autoresearch-worker:dev + imagePullPolicy: IfNotPresent + ports: + - containerPort: 8080 + env: + - name: DATA_DIR + value: /data + - name: AUTORESEARCH_REPO + value: /data/autoresearch + - name: EXPERIMENT_TIMEOUT_SECONDS + value: "300" + resources: + limits: + nvidia.com/gpu: 1 + volumeMounts: + - name: worker-data + mountPath: /data + volumes: + - name: worker-data + persistentVolumeClaim: + claimName: autoresearch-worker-data +--- +apiVersion: v1 +kind: Service +metadata: + name: autoresearch-worker + namespace: autoresearch +spec: + selector: + app: autoresearch-worker + ports: + - name: http + port: 8080 + targetPort: 8080 +``` + +After the Service exists, expose it with: + +```bash +obol sell http autoresearch-worker \ + --namespace autoresearch \ + --upstream autoresearch-worker \ + --port 8080 \ + --health-path /health \ + --wallet 0xYourWalletAddress \ + --chain base-sepolia \ + --per-hour 0.50 \ + --path /services/autoresearch-worker \ + --register \ + --register-name "GPU Worker Alpha" \ + --register-description "A GPU worker for paid autoresearch experiments" \ + --register-skills devops_mlops/model_versioning \ + --register-domains research_and_development/scientific_research +``` diff --git a/internal/embed/skills/autoresearch-worker/references/worker-api.md b/internal/embed/skills/autoresearch-worker/references/worker-api.md new file mode 100644 index 00000000..7454c8c0 --- /dev/null +++ b/internal/embed/skills/autoresearch-worker/references/worker-api.md @@ -0,0 +1,144 @@ +# Autoresearch Worker API + +The worker API is a small synchronous HTTP service for selling GPU-backed autoresearch experiments. + +## Endpoints + +### `GET /health` and `GET /healthz` + +Returns basic health/config status. + +Example response: + +```json +{ + "status": "ok", + "busy": false, + "repo": "/data/autoresearch", + "timeoutSeconds": 300 +} +``` + +### `GET /status` + +Returns the live worker status including current, last, and best results. + +### `GET /best` + +Returns the best result seen so far, or `404` if no completed result exists yet. + +### `GET /experiments/` + +Returns the stored JSON result for one experiment. + +### `POST /experiment` + +Runs an experiment. Expected request body: + +```json +{ + "train_py": "print('training logic here')", + "config": { + "batch_size": 64, + "learning_rate": 0.001 + }, + "experiment_id": "optional-custom-id" +} +``` + +Special-case probe body: + +```json +{ + "probe": true +} +``` + +This returns `200` with a small readiness payload when the request reaches the worker directly. In the x402-gated flow, unauthenticated probe requests should usually be intercepted before the worker and turned into `402 Payment Required`. + +## Result Shape + +Example response: + +```json +{ + "experiment_id": "exp-20260312-deadbeef", + "status": "completed", + "return_code": 0, + "val_bpb": 1.0234, + "train_hash": "sha256:...", + "artifact_path": "/data/autoresearch-worker/results/exp-20260312-deadbeef/work/model.gguf", + "log_path": "/data/autoresearch-worker/results/exp-20260312-deadbeef/run.log", + "startedAt": "2026-03-12T12:00:00+00:00", + "finishedAt": "2026-03-12T12:05:00+00:00", + "durationSeconds": 300.0, + "config": {} +} +``` + +`status` may be: +- `completed` +- `failed` +- `timeout` + +## Data Layout + +The worker stores state under: + +```text +$DATA_DIR/autoresearch-worker/ + best.json + results.jsonl + results/ + / + config.json + train.py + run.log + result.json + work/ +``` + +## Deployment Notes + +## Recommended: k3s on the GPU host + +This is the cleanest production path because: +- the worker is reachable through a Kubernetes Service +- `obol sell http` can point at that service directly +- GPU access can be provided via the host's Kubernetes GPU setup + +### Minimal deployment pattern + +1. Build the image: + +```bash +docker build -f Dockerfile.worker -t autoresearch-worker:dev . +``` + +2. Run it on a GPU host with the autoresearch repo mounted at `/data/autoresearch`. + +3. Expose it as a Kubernetes Service named `autoresearch-worker` in namespace `autoresearch`. + +4. Monetize it with: + +```bash +obol sell http autoresearch-worker \ + --namespace autoresearch \ + --upstream autoresearch-worker \ + --port 8080 \ + --health-path /health \ + --wallet 0xYourWalletAddress \ + --chain base-sepolia \ + --per-hour 0.50 \ + --path /services/autoresearch-worker \ + --register \ + --register-name "GPU Worker Alpha" \ + --register-description "A GPU worker for paid autoresearch experiments" \ + --register-skills devops_mlops/model_versioning \ + --register-domains research_and_development/scientific_research +``` + +## Security Note + +This API executes submitted `train.py` code. Treat the worker as dedicated, untrusted-code infrastructure. +Do not run it on a machine that also hosts unrelated workloads or sensitive data. diff --git a/internal/embed/skills/autoresearch-worker/scripts/worker_api.py b/internal/embed/skills/autoresearch-worker/scripts/worker_api.py new file mode 100644 index 00000000..1b4d953c --- /dev/null +++ b/internal/embed/skills/autoresearch-worker/scripts/worker_api.py @@ -0,0 +1,477 @@ +#!/usr/bin/env python3 +"""worker_api.py -- GPU worker HTTP API for autoresearch experiments. + +Runs submitted train.py experiments one at a time, stores results on disk, and +exposes a minimal HTTP API suitable for x402-gated selling via `obol sell http`. + +Endpoints: + GET /health, /healthz Worker health and config summary + GET /status Busy/idle status plus last/best results + GET /best Best known result + GET /experiments/ Fetch a stored experiment result + POST /experiment Submit a train.py experiment (or probe with {"probe": true}) + +Environment: + DATA_DIR Base directory for worker state (default: /data) + AUTORESEARCH_REPO Template repo/workdir copied per experiment + EXPERIMENT_TIMEOUT_SECONDS Max runtime per experiment (default: 300) + TRAIN_COMMAND Command to run inside experiment workdir (default: uv run train.py) +""" + +from __future__ import annotations + +import argparse +import csv +import hashlib +import json +import os +import re +import shlex +import shutil +import subprocess +import sys +import threading +import time +from dataclasses import dataclass +from datetime import datetime, timezone +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from pathlib import Path +from typing import Any +from urllib.parse import urlparse +import uuid + +CHECKPOINT_EXTENSIONS = (".gguf", ".safetensors", ".pt", ".pth", ".bin") +COPY_IGNORE = shutil.ignore_patterns( + ".git", + ".venv", + "__pycache__", + ".pytest_cache", + ".mypy_cache", + "worktrees", + "queue", + "results", +) + + +class BusyError(RuntimeError): + pass + + +@dataclass +class WorkerConfig: + repo: Path + data_dir: Path + command: list[str] + timeout_seconds: int + + +def utc_now() -> str: + return datetime.now(timezone.utc).isoformat() + + +def sha256_text(text: str) -> str: + return f"sha256:{hashlib.sha256(text.encode()).hexdigest()}" + + +def generate_experiment_id() -> str: + return f"exp-{datetime.now(timezone.utc).strftime('%Y%m%d')}-{uuid.uuid4().hex[:8]}" + + +def extract_val_bpb(log_text: str, workdir: Path | None = None) -> float | None: + """Extract val_bpb from log output or results.tsv. + + Prefers structured JSON payloads, then common textual patterns, then the + smallest val_bpb recorded in a results.tsv file if present. + """ + stripped = (log_text or "").strip() + if stripped: + # JSON payloads emitted as either a top-level object or metrics object. + try: + data = json.loads(stripped) + if isinstance(data, dict): + if data.get("val_bpb") is not None: + return float(data["val_bpb"]) + metrics = data.get("metrics") + if isinstance(metrics, dict) and metrics.get("val_bpb") is not None: + return float(metrics["val_bpb"]) + except (json.JSONDecodeError, ValueError, TypeError): + pass + + patterns = [ + r"\bval_bpb\b\s*[:=]\s*([0-9]+(?:\.[0-9]+)?)", + r"\bbest[_ ]val[_ ]bpb\b\s*[:=]\s*([0-9]+(?:\.[0-9]+)?)", + r'"val_bpb"\s*:\s*([0-9]+(?:\.[0-9]+)?)', + ] + for pattern in patterns: + match = re.search(pattern, stripped, flags=re.IGNORECASE) + if match: + try: + return float(match.group(1)) + except ValueError: + pass + + if workdir is not None: + results_path = Path(workdir) / "results.tsv" + if results_path.exists(): + best: float | None = None + with open(results_path, "r", encoding="utf-8") as f: + reader = csv.DictReader(f, delimiter="\t") + for row in reader: + value = row.get("val_bpb") + if not value: + continue + try: + parsed = float(value) + except ValueError: + continue + if best is None or parsed < best: + best = parsed + return best + + return None + + +def find_artifact(root: Path) -> Path | None: + """Return the newest checkpoint-like artifact under root.""" + candidates: list[Path] = [] + for ext in CHECKPOINT_EXTENSIONS: + candidates.extend(root.rglob(f"*{ext}")) + candidates = [p for p in candidates if p.is_file() and ".git" not in p.parts] + if not candidates: + return None + return max(candidates, key=lambda p: p.stat().st_mtime) + + +def choose_best_result(existing: dict[str, Any] | None, candidate: dict[str, Any] | None) -> dict[str, Any] | None: + """Pick the better result by lower val_bpb, treating missing values as worse.""" + if not candidate: + return existing + if candidate.get("val_bpb") is None: + return existing + if not existing or existing.get("val_bpb") is None: + return candidate + try: + if float(candidate["val_bpb"]) < float(existing["val_bpb"]): + return candidate + except (ValueError, TypeError): + return existing + return existing + + +class WorkerState: + def __init__(self, config: WorkerConfig): + self.config = config + self.lock = threading.Lock() + self.busy = False + self.current: dict[str, Any] | None = None + self.last_result: dict[str, Any] | None = None + self.base_dir = config.data_dir / "autoresearch-worker" + self.results_dir = self.base_dir / "results" + self.best_path = self.base_dir / "best.json" + self.history_path = self.base_dir / "results.jsonl" + self.base_dir.mkdir(parents=True, exist_ok=True) + self.results_dir.mkdir(parents=True, exist_ok=True) + self.best_result = self._load_json(self.best_path) + + def _load_json(self, path: Path) -> dict[str, Any] | None: + if not path.exists(): + return None + try: + return json.loads(path.read_text(encoding="utf-8")) + except (OSError, json.JSONDecodeError): + return None + + def snapshot(self) -> dict[str, Any]: + with self.lock: + return { + "status": "busy" if self.busy else "idle", + "current": self.current, + "last": self.last_result, + "best": self.best_result, + "repo": str(self.config.repo), + "command": self.config.command, + "timeoutSeconds": self.config.timeout_seconds, + } + + def record_result(self, result: dict[str, Any]) -> None: + exp_dir = self.results_dir / result["experiment_id"] + exp_dir.mkdir(parents=True, exist_ok=True) + (exp_dir / "result.json").write_text(json.dumps(result, indent=2), encoding="utf-8") + with open(self.history_path, "a", encoding="utf-8") as f: + f.write(json.dumps(result) + "\n") + best = choose_best_result(self.best_result, result) + if best is not self.best_result and best is not None: + self.best_result = best + self.best_path.write_text(json.dumps(best, indent=2), encoding="utf-8") + self.last_result = result + + def run_experiment(self, train_py_source: str, config_overrides: dict[str, Any] | None = None, experiment_id: str | None = None) -> dict[str, Any]: + if not train_py_source.strip(): + raise ValueError("train_py is required") + + with self.lock: + if self.busy: + raise BusyError("worker is already running an experiment") + exp_id = experiment_id or generate_experiment_id() + self.busy = True + self.current = { + "experiment_id": exp_id, + "startedAt": utc_now(), + } + + try: + result = self._run_experiment_impl(exp_id, train_py_source, config_overrides or {}) + self.record_result(result) + return result + finally: + with self.lock: + self.busy = False + self.current = None + + def _run_experiment_impl(self, experiment_id: str, train_py_source: str, config_overrides: dict[str, Any]) -> dict[str, Any]: + exp_dir = self.results_dir / experiment_id + workdir = exp_dir / "work" + exp_dir.mkdir(parents=True, exist_ok=True) + + started = time.time() + started_at = utc_now() + train_hash = sha256_text(train_py_source) + artifact: Path | None = None + status = "completed" + return_code = 0 + stdout = "" + stderr = "" + note = None + + if self.config.repo.exists(): + shutil.copytree(self.config.repo, workdir, ignore=COPY_IGNORE) + else: + workdir.mkdir(parents=True, exist_ok=True) + + train_path = workdir / "train.py" + train_path.write_text(train_py_source, encoding="utf-8") + (exp_dir / "train.py").write_text(train_py_source, encoding="utf-8") + + config_path = exp_dir / "config.json" + config_path.write_text(json.dumps(config_overrides, indent=2), encoding="utf-8") + + env = os.environ.copy() + env.setdefault("PYTHONUNBUFFERED", "1") + env["AUTORESEARCH_EXPERIMENT_ID"] = experiment_id + env["AUTORESEARCH_EXPERIMENT_DIR"] = str(exp_dir) + env["AUTORESEARCH_EXPERIMENT_CONFIG"] = str(config_path) + + try: + completed = subprocess.run( + self.config.command, + cwd=workdir, + env=env, + capture_output=True, + text=True, + timeout=self.config.timeout_seconds, + check=False, + ) + stdout = completed.stdout + stderr = completed.stderr + return_code = completed.returncode + if completed.returncode != 0: + status = "failed" + note = f"command exited with status {completed.returncode}" + except subprocess.TimeoutExpired as exc: + stdout = exc.stdout or "" + stderr = exc.stderr or "" + return_code = -1 + status = "timeout" + note = f"experiment exceeded timeout of {self.config.timeout_seconds}s" + except OSError as exc: + stderr = str(exc) + return_code = -1 + status = "failed" + note = f"failed to execute command: {exc}" + + log_text = stdout + if stderr: + log_text += ("\n--- stderr ---\n" if log_text else "") + stderr + log_path = exp_dir / "run.log" + log_path.write_text(log_text, encoding="utf-8") + + val_bpb = extract_val_bpb(log_text, workdir) + artifact = find_artifact(workdir) + + result = { + "experiment_id": experiment_id, + "status": status, + "return_code": return_code, + "val_bpb": val_bpb, + "train_hash": train_hash, + "artifact_path": str(artifact) if artifact else None, + "log_path": str(log_path), + "startedAt": started_at, + "finishedAt": utc_now(), + "durationSeconds": round(time.time() - started, 3), + "config": config_overrides, + } + if note: + result["note"] = note + return result + + +class WorkerHandler(BaseHTTPRequestHandler): + state: WorkerState | None = None + + def log_message(self, format: str, *args: Any) -> None: + sys.stderr.write("%s - - [%s] %s\n" % (self.address_string(), self.log_date_time_string(), format % args)) + + def _json(self, status: int, payload: dict[str, Any]) -> None: + body = json.dumps(payload).encode("utf-8") + self.send_response(status) + self.send_header("Content-Type", "application/json") + self.send_header("Content-Length", str(len(body))) + self.end_headers() + self.wfile.write(body) + + def _read_json(self) -> dict[str, Any]: + length = int(self.headers.get("Content-Length", "0") or "0") + if length <= 0: + return {} + raw = self.rfile.read(length) + if not raw: + return {} + try: + data = json.loads(raw) + except json.JSONDecodeError as exc: + raise ValueError(f"invalid JSON body: {exc}") from exc + if not isinstance(data, dict): + raise ValueError("JSON body must be an object") + return data + + def do_GET(self) -> None: + assert self.state is not None + path = urlparse(self.path).path + if path in ("/health", "/healthz"): + self._json(200, { + "status": "ok", + "busy": self.state.snapshot()["status"] == "busy", + "repo": str(self.state.config.repo), + "timeoutSeconds": self.state.config.timeout_seconds, + }) + return + if path == "/status": + self._json(200, self.state.snapshot()) + return + if path == "/best": + if self.state.best_result is None: + self._json(404, {"error": "no completed experiments yet"}) + return + self._json(200, self.state.best_result) + return + if path.startswith("/experiments/"): + exp_id = path.rsplit("/", 1)[-1] + if not re.fullmatch(r'[a-zA-Z0-9_-]+', exp_id): + self._json(400, {"error": "invalid experiment id"}) + return + result_path = self.state.results_dir / exp_id / "result.json" + if not result_path.exists(): + self._json(404, {"error": f"experiment {exp_id} not found"}) + return + self._json(200, json.loads(result_path.read_text(encoding="utf-8"))) + return + self._json(404, {"error": f"unknown endpoint: {path}"}) + + def do_POST(self) -> None: + assert self.state is not None + path = urlparse(self.path).path + if path != "/experiment": + self._json(404, {"error": f"unknown endpoint: {path}"}) + return + + try: + payload = self._read_json() + except ValueError as exc: + self._json(400, {"error": str(exc)}) + return + + if payload.get("probe") is True: + self._json(200, { + "status": "ok", + "probe": True, + "busy": self.state.snapshot()["status"] == "busy", + "timeoutSeconds": self.state.config.timeout_seconds, + }) + return + + train_py = payload.get("train_py", payload.get("trainPy")) + if not isinstance(train_py, str) or not train_py.strip(): + self._json(400, {"error": "train_py string is required"}) + return + + config_overrides = payload.get("config") + if config_overrides is None: + config_overrides = {} + if not isinstance(config_overrides, dict): + self._json(400, {"error": "config must be a JSON object when provided"}) + return + + experiment_id = payload.get("experiment_id", payload.get("experimentId")) + if experiment_id is not None and not isinstance(experiment_id, str): + self._json(400, {"error": "experiment_id must be a string when provided"}) + return + if experiment_id is not None and not re.fullmatch(r'[a-zA-Z0-9_-]+', experiment_id): + self._json(400, {"error": "experiment_id must contain only alphanumeric characters, hyphens, or underscores"}) + return + + try: + result = self.state.run_experiment(train_py, config_overrides, experiment_id) + except BusyError as exc: + self._json(409, {"error": str(exc), "status": "busy"}) + return + except ValueError as exc: + self._json(400, {"error": str(exc)}) + return + except Exception as exc: + self._json(500, {"error": f"internal worker error: {exc}"}) + return + + self._json(200, result) + + +def make_server(state: WorkerState, host: str, port: int) -> ThreadingHTTPServer: + WorkerHandler.state = state + return ThreadingHTTPServer((host, port), WorkerHandler) + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Run the autoresearch GPU worker API.") + parser.add_argument("command", nargs="?", default="serve", choices=["serve"], help="Command to run (default: serve)") + parser.add_argument("--repo", default=os.environ.get("AUTORESEARCH_REPO", "/data/autoresearch"), help="Path to the base autoresearch repo/workdir") + parser.add_argument("--data-dir", default=os.environ.get("DATA_DIR", "/data"), help="Directory for worker state and results") + parser.add_argument("--host", default="0.0.0.0", help="Listen host") + parser.add_argument("--port", type=int, default=8080, help="Listen port") + parser.add_argument("--timeout", type=int, default=int(os.environ.get("EXPERIMENT_TIMEOUT_SECONDS", "300")), help="Max experiment runtime in seconds") + parser.add_argument("--train-command", default=os.environ.get("TRAIN_COMMAND", "uv run train.py"), help="Command used to run train.py") + return parser.parse_args() + + +def main() -> None: + args = parse_args() + command = shlex.split(args.train_command) + config = WorkerConfig( + repo=Path(args.repo), + data_dir=Path(args.data_dir), + command=command, + timeout_seconds=args.timeout, + ) + state = WorkerState(config) + server = make_server(state, args.host, args.port) + print(f"autoresearch-worker listening on http://{args.host}:{args.port}") + print(f"repo={config.repo} data_dir={config.data_dir} timeout={config.timeout_seconds}s command={' '.join(config.command)}") + try: + server.serve_forever() + except KeyboardInterrupt: + print("\nshutting down worker") + finally: + server.server_close() + + +if __name__ == "__main__": + main() diff --git a/internal/embed/skills/autoresearch/SKILL.md b/internal/embed/skills/autoresearch/SKILL.md new file mode 100644 index 00000000..da4d8b09 --- /dev/null +++ b/internal/embed/skills/autoresearch/SKILL.md @@ -0,0 +1,134 @@ +--- +name: autoresearch +description: "Run autonomous LLM optimization experiments (autoresearch) and publish optimized models for paid inference via x402." +metadata: { "openclaw": { "emoji": "🧪", "requires": { "bins": ["python3", "uv"] } } } +--- + +# Autoresearch + +Autonomous LLM optimization: the agent iterates on `train.py`, runs 5-minute GPU experiments, measures validation bits-per-byte (val_bpb), and publishes the best checkpoint as a sellable Ollama model. + +## When to Use + +- Optimizing a base model for a specific domain or task +- Running automated training experiments to improve val_bpb +- Publishing an optimized model checkpoint to Ollama +- Selling an optimized model via x402 payment-gated inference + +## When NOT to Use + +- Selling an existing model without optimization — use `sell` +- Buying remote inference — use `buy-inference` +- Cluster diagnostics — use `obol-stack` + +## Quick Start + +### 1. Prepare Data + +Place your training and validation data in the autoresearch working directory: + +``` +autoresearch/ + train.bin # training data (tokenized) + val.bin # validation data (tokenized) + train.py # training script (agent modifies this) + results.tsv # experiment log (appended by each run) +``` + +### 2. Run Experiments + +The agent modifies `train.py` and runs experiments in a loop. Each experiment: + +- Has a **5-minute time budget** on GPU +- Produces a checkpoint and a val_bpb measurement +- Is tracked as a git commit with status (keep/discard) in `results.tsv` + +The `results.tsv` file is tab-separated with columns: + +``` +commit_hash val_bpb status description +a1b2c3d 1.042 keep baseline transformer +e4f5g6h 1.038 keep added RMSNorm +i7j8k9l 1.051 discard unstable lr schedule +``` + +### 3. Publish the Best Model + +Once experiments are complete, use `publish.py` to find the best checkpoint, register it with Ollama, and optionally sell it: + +```bash +# Publish to Ollama only +python3 scripts/publish.py /path/to/autoresearch + +# Publish and sell via x402 +python3 scripts/publish.py /path/to/autoresearch \ + --sell \ + --wallet 0xYourWalletAddress \ + --price 0.002 \ + --chain base-sepolia +``` + +## Commands + +| Command | Description | +|---------|-------------| +| `publish.py ` | Find best experiment, create Ollama model, generate provenance | +| `publish.py --sell --wallet --price

--chain ` | Publish and sell via `obol sell inference` | + +## How It Works + +1. **Experiment loop**: The agent edits `train.py`, runs training for up to 5 minutes, measures val_bpb on the validation set, and commits the result with a keep/discard verdict. + +2. **Selection**: `publish.py` reads `results.tsv`, filters for `status=keep`, and selects the experiment with the lowest val_bpb (lower is better — fewer bits per byte means better compression / prediction). + +3. **Provenance**: A JSON provenance file is generated with: + - `framework`: training framework used + - `metricName`: metric identifier (`val_bpb`) + - `metricValue`: winning metric value as a string + - `trainHash`: `sha256:` hash of the `train.py` at the winning commit + - `paramCount`: model parameter count as a string + - `experimentId`: git commit hash of the winning experiment + +4. **Ollama registration**: A Modelfile is generated from the checkpoint and `ollama create` registers the model locally. + +5. **Sell (optional)**: If `--sell` is passed, runs `obol sell inference` with the `--provenance-file` flag pointing at the provenance JSON so buyers can verify optimization lineage. + +## Architecture + +``` +Agent (autoresearch loop) + | + +-- edit train.py + +-- run experiment (5-min budget) + +-- measure val_bpb + +-- commit results.tsv + | + v +publish.py + | + +-- read results.tsv → best experiment + +-- git show :train.py → SHA-256 trainHash + +-- generate provenance.json + +-- generate Modelfile → ollama create + +-- (optional) obol sell inference --provenance-file +``` + +## Constraints + +- **Python stdlib + uv** — no pip install; uv for environment management +- **5-minute time budget** — each experiment must complete within 5 minutes +- **GPU required** — training runs on local GPU (Ollama must have GPU access) +- **GGUF checkpoint required** — Ollama only accepts GGUF format; convert other formats (`.pt`, `.safetensors`) with `llama.cpp/convert_hf_to_gguf.py` +- **Git repo required** — autoresearch directory must be a git repository for commit tracking +- **results.tsv format** — tab-separated: `commit_hash`, `val_bpb`, `status`, `description` + +## OASF Registration + +When registering an autoresearch-optimized model on-chain via ERC-8004: + +- **Skills**: `devops_mlops/model_versioning` +- **Domains**: `research_and_development/scientific_research` + +## References + +- `references/autoresearch-overview.md` — val_bpb metric, time budget, and the train.py modification loop diff --git a/internal/embed/skills/autoresearch/scripts/publish.py b/internal/embed/skills/autoresearch/scripts/publish.py new file mode 100755 index 00000000..799f597b --- /dev/null +++ b/internal/embed/skills/autoresearch/scripts/publish.py @@ -0,0 +1,300 @@ +#!/usr/bin/env python3 +"""Publish the best autoresearch experiment to Ollama and optionally sell via x402. + +Usage: + python3 publish.py

+ python3 publish.py --sell --wallet 0x... --price 0.002 --chain base-sepolia +""" + +import argparse +import csv +import hashlib +import json +import os +import subprocess +import sys +import tempfile +from pathlib import Path + + +def die(msg: str) -> None: + print(f"ERROR: {msg}", file=sys.stderr) + sys.exit(1) + + +def run(cmd: list[str], cwd: str | None = None, capture: bool = True) -> str: + """Run a command and return stdout. Exits on failure.""" + try: + result = subprocess.run( + cmd, + cwd=cwd, + capture_output=capture, + text=True, + check=True, + ) + return result.stdout.strip() if capture else "" + except FileNotFoundError: + die(f"command not found: {cmd[0]}") + except subprocess.CalledProcessError as exc: + detail = exc.stderr.strip() if exc.stderr else str(exc) + die(f"command failed: {' '.join(cmd)}\n{detail}") + return "" # unreachable, keeps type checkers happy + + +def read_results(results_path: Path) -> list[dict]: + """Read results.tsv and return rows as dicts.""" + if not results_path.exists(): + die(f"results.tsv not found at {results_path}") + + rows: list[dict] = [] + with open(results_path, "r") as f: + reader = csv.DictReader(f, delimiter="\t") + for row in reader: + rows.append(row) + + if not rows: + die("results.tsv is empty") + return rows + + +def find_best_experiment(rows: list[dict]) -> dict: + """Find the experiment with status=keep and lowest val_bpb.""" + kept = [r for r in rows if r.get("status", "").strip().lower() == "keep"] + if not kept: + die("no experiments with status=keep found in results.tsv") + + best = None + best_bpb = float("inf") + for row in kept: + try: + bpb = float(row["val_bpb"]) + except (KeyError, ValueError): + continue + if bpb < best_bpb: + best_bpb = bpb + best = row + + if best is None: + die("no valid val_bpb values found in kept experiments") + return best + + +def get_train_hash(workdir: str, commit: str) -> str: + """Get SHA-256 of train.py at the given commit.""" + content = run(["git", "show", f"{commit}:train.py"], cwd=workdir) + return hashlib.sha256(content.encode()).hexdigest() + + +def get_param_count(workdir: str, commit: str) -> int | None: + """Try to extract param count from checkpoint metadata. Returns None if unavailable.""" + # Look for a metadata.json or similar in the commit tree + try: + tree = run(["git", "ls-tree", "--name-only", commit], cwd=workdir) + for fname in tree.splitlines(): + if fname in ("metadata.json", "config.json"): + content = run(["git", "show", f"{commit}:{fname}"], cwd=workdir) + meta = json.loads(content) + for key in ("param_count", "n_params", "num_parameters", "total_params"): + if key in meta: + return int(meta[key]) + except Exception: + pass + return None + + +def find_checkpoint(workdir: str, commit: str) -> Path | None: + """Find the model checkpoint file in the working directory for a given commit.""" + # Check common checkpoint patterns + try: + tree = run(["git", "ls-tree", "--name-only", "-r", commit], cwd=workdir) + except SystemExit: + tree = "" + + checkpoint_patterns = (".pt", ".pth", ".bin", ".safetensors", ".gguf") + for fname in tree.splitlines(): + if any(fname.endswith(ext) for ext in checkpoint_patterns): + # Extract the file from the commit + ckpt_path = Path(workdir) / fname + if ckpt_path.exists(): + return ckpt_path + + # Fallback: look in working directory for checkpoint files + for ext in checkpoint_patterns: + candidates = list(Path(workdir).glob(f"**/*{ext}")) + if candidates: + # Return most recently modified + return max(candidates, key=lambda p: p.stat().st_mtime) + + return None + + +def generate_provenance( + workdir: str, + best: dict, + train_hash: str, + param_count: int | None, +) -> Path: + """Generate canonical provenance JSON and write it to the workdir.""" + provenance = { + "framework": "autoresearch", + "metricName": "val_bpb", + "metricValue": str(best["val_bpb"]), + "experimentId": best["commit_hash"].strip(), + "trainHash": f"sha256:{train_hash}", + } + if param_count is not None: + provenance["paramCount"] = str(param_count) + + out_path = Path(workdir) / "provenance.json" + with open(out_path, "w") as f: + json.dump(provenance, f, indent=2) + print(f"Provenance written to {out_path}") + return out_path + + +def create_modelfile(checkpoint_path: Path, workdir: str) -> Path: + """Create an Ollama Modelfile from a checkpoint.""" + modelfile_path = Path(workdir) / "Modelfile" + content = f'FROM {checkpoint_path}\n' + with open(modelfile_path, "w") as f: + f.write(content) + print(f"Modelfile written to {modelfile_path}") + return modelfile_path + + +def ollama_create(model_name: str, modelfile_path: Path) -> None: + """Register the model with Ollama.""" + print(f"Creating Ollama model: {model_name}") + run( + ["ollama", "create", model_name, "-f", str(modelfile_path)], + capture=False, + ) + print(f"Model '{model_name}' registered with Ollama") + + +def sell_inference( + model_name: str, + provenance_path: Path, + wallet: str, + price: str, + chain: str, +) -> None: + """Run obol sell inference to monetize the model.""" + cmd = [ + "obol", "sell", "inference", model_name, + "--model", model_name, + "--price", price, + "--wallet", wallet, + "--chain", chain, + "--provenance-file", str(provenance_path), + ] + print(f"Selling model: {' '.join(cmd)}") + run(cmd, capture=False) + print(f"Model '{model_name}' listed for paid inference") + + +def main() -> None: + parser = argparse.ArgumentParser( + description="Publish the best autoresearch experiment to Ollama and optionally sell via x402.", + ) + parser.add_argument( + "dir", + help="Path to autoresearch working directory (must contain results.tsv)", + ) + parser.add_argument( + "--name", + default=None, + help="Ollama model name (default: autoresearch-)", + ) + parser.add_argument( + "--sell", + action="store_true", + help="Also sell the model via obol sell inference", + ) + parser.add_argument( + "--wallet", + default=None, + help="USDC recipient wallet address (required with --sell)", + ) + parser.add_argument( + "--price", + default=None, + help="Price per request in USDC (required with --sell)", + ) + parser.add_argument( + "--chain", + default="base-sepolia", + help="Payment chain (default: base-sepolia)", + ) + args = parser.parse_args() + + workdir = os.path.abspath(args.dir) + if not os.path.isdir(workdir): + die(f"directory not found: {workdir}") + + results_path = Path(workdir) / "results.tsv" + + # Verify git repo + if not (Path(workdir) / ".git").exists(): + die(f"{workdir} is not a git repository (autoresearch requires git for commit tracking)") + + # Step 1: Find best experiment + print("Reading results.tsv...") + rows = read_results(results_path) + best = find_best_experiment(rows) + commit = best["commit_hash"].strip() + bpb = float(best["val_bpb"]) + desc = best.get("description", "").strip() + print(f"Best experiment: commit={commit[:8]} val_bpb={bpb:.4f} ({desc})") + + # Step 2: Compute train.py hash + print("Computing train.py hash...") + train_hash = get_train_hash(workdir, commit) + print(f"trainHash (SHA-256): {train_hash[:16]}...") + + # Step 3: Get param count (optional) + param_count = get_param_count(workdir, commit) + if param_count is not None: + print(f"Parameter count: {param_count:,}") + + # Step 4: Generate provenance + provenance_path = generate_provenance(workdir, best, train_hash, param_count) + + # Step 5: Find checkpoint and create Ollama model + checkpoint = find_checkpoint(workdir, commit) + if checkpoint is None: + die( + "no model checkpoint found (.pt, .pth, .bin, .safetensors, .gguf). " + "Ensure the checkpoint is committed or present in the working directory." + ) + print(f"Using checkpoint: {checkpoint}") + + if not str(checkpoint).endswith(".gguf"): + die( + f"checkpoint {checkpoint.name} is not in GGUF format. " + "Ollama requires GGUF files. Convert with:\n" + " python llama.cpp/convert_hf_to_gguf.py --outfile model.gguf" + ) + + model_name = args.name or f"autoresearch-{commit[:8]}" + modelfile_path = create_modelfile(checkpoint, workdir) + ollama_create(model_name, modelfile_path) + + # Step 6: Optionally sell + if args.sell: + if not args.wallet: + die("--wallet is required when using --sell") + if not args.price: + die("--price is required when using --sell") + sell_inference(model_name, provenance_path, args.wallet, args.price, args.chain) + + print("\nDone.") + print(f" Model: {model_name}") + print(f" val_bpb: {bpb:.4f}") + print(f" Provenance: {provenance_path}") + if args.sell: + print(f" Selling: yes (chain={args.chain}, price={args.price} USDC)") + + +if __name__ == "__main__": + main() diff --git a/internal/embed/skills/monetize-guide/SKILL.md b/internal/embed/skills/monetize-guide/SKILL.md new file mode 100644 index 00000000..0ae5dc52 --- /dev/null +++ b/internal/embed/skills/monetize-guide/SKILL.md @@ -0,0 +1,282 @@ +--- +name: monetize-guide +description: "End-to-end guide for monetizing GPU resources or HTTP services through obol-stack. Covers pre-flight checks, model detection, pricing research, selling via x402, ERC-8004 registration, and verification. Use this skill when the user wants to monetize their machine." +metadata: { "openclaw": { "emoji": "🚀", "requires": { "bins": ["python3"] } } } +--- + +# Monetize Guide + +Step-by-step guide to expose local GPU resources or HTTP services as x402 payment-gated endpoints with on-chain discovery via ERC-8004. + +## When to Use + +- User says "monetize my machine", "sell my GPU", or "expose my service for payments" +- Setting up a new paid endpoint from scratch +- Need to figure out what to sell and at what price + +## When NOT to Use + +- Managing existing offers (list/status/delete) — use `sell` directly +- Buying inference from others — use `buy-inference` +- Cluster diagnostics — use `obol-stack` + +## Workflow + +Follow these phases in order. Stop and ask the user for confirmation before executing phase 4. + +### Phase 1: Pre-flight Checks + +Verify the environment is ready before proceeding. + +```bash +# 1. Check cluster is running +obol kubectl get nodes + +# 2. Check the agent is initialized (has RBAC for monetization) +obol kubectl get clusterrolebinding openclaw-monetize-read-binding -o jsonpath='{.subjects}' + +# 3. Get the wallet address (auto-generated by agent) +python3 /data/.openclaw/skills/ethereum-local-wallet/scripts/signer.py accounts +``` + +**If cluster is not running**: Tell the user to run `obol stack up` first. Do NOT run it yourself — it takes several minutes and changes system state. + +**If agent has no RBAC subjects**: Tell the user to run `obol agent init`. + +**If no wallet address**: The wallet is created during `obol stack up`. If missing, the stack may not have completed setup. + +### Phase 2: Detect What Can Be Sold + +#### For GPU / Inference + +```bash +# Check what Ollama models are available locally +curl -s http://localhost:11434/api/tags | python3 -c " +import json, sys +data = json.load(sys.stdin) +for m in data.get('models', []): + size_gb = m.get('size', 0) / 1e9 + print(f\" {m['name']:30s} {size_gb:.1f} GB\") +" +``` + +Report the available models to the user. If no models are found, suggest they pull one: +```bash +ollama pull qwen3.5:4b # Small, fast +ollama pull qwen3.5:9b # Medium, good quality +``` + +#### For External LAN Resources (GPU servers, vLLM, etc.) + +Ask the user if they have a GPU server or inference endpoint on their local network. If yes, get: +- The endpoint URL (e.g., `http://192.168.0.202:8000/v1`) +- The model name served at that endpoint + +Verify it's reachable and OpenAI-compatible: +```bash +# Probe the external endpoint +curl -s /models | python3 -c " +import json, sys +data = json.load(sys.stdin) +for m in data.get('data', []): + print(f\" {m['id']}\") +" +``` + +If reachable, this endpoint can be sold by bridging it through LiteLLM (see Phase 4). + +#### For HTTP Services (in-cluster) + +```bash +# List services in the cluster that could be monetized +obol kubectl get svc -A --no-headers | grep -v 'kube-system\|traefik\|x402\|monitoring\|erpc\|obol-frontend' +``` + +Ask the user which service they want to expose and on which port. + +### Phase 3: Research Pricing + +Query the ERC-8004 registry to see what comparable services charge. + +```bash +# Search for registered agents on Base Sepolia +python3 /data/.openclaw/skills/discovery/scripts/discovery.py search --limit 10 + +# For each agent with x402Support, fetch their registration to see pricing +python3 /data/.openclaw/skills/discovery/scripts/discovery.py uri +``` + +**Pricing guidelines** (present these to the user with your research): + +| Service Type | Typical Range | Notes | +|-------------|---------------|-------| +| LLM inference (small, <4B) | 0.0005–0.002 USDC/req | Fast, low compute | +| LLM inference (medium, 4-14B) | 0.001–0.005 USDC/req | Good quality/cost balance | +| LLM inference (large, >14B) | 0.005–0.02 USDC/req | High quality, slower | +| Data API / indexer | 0.0001–0.001 USDC/req | Depends on query complexity | +| Compute-heavy (GPU hours) | 0.10–1.00 USDC/hour | Fine-tuning, training | + +**Always present your research and recommendation to the user and ask them to confirm the price before proceeding.** + +### Phase 4: Sell the Service + +Only proceed after the user has confirmed the price. + +#### Inference (Ollama model) + +```bash +obol sell inference \ + --model \ + --price \ + --register \ + --register-name "" \ + --register-description "" \ + --register-skills natural_language_processing/natural_language_generation/text_completion \ + --register-domains technology/data_science +``` + +The `--wallet` and `--chain` will be auto-resolved (remote-signer wallet, base-sepolia default). + +#### External LAN Resource (GPU server, vLLM, etc.) + +Two steps: first bridge the endpoint into LiteLLM, then sell LiteLLM. + +```bash +# Step A: Add the external endpoint to LiteLLM +obol model setup custom --name \ + --endpoint \ + --model "" + +# Step B: Sell it through LiteLLM +obol sell http \ + --upstream litellm \ + --port 4000 \ + --namespace llm \ + --per-request \ + --wallet \ + --chain base-sepolia \ + --health-path /health/liveliness \ + --register \ + --register-name "" \ + --register-description "" \ + --register-skills natural_language_processing/natural_language_generation/text_completion \ + --register-domains technology/data_science +``` + +The `--endpoint` must include `/v1` if the upstream is an OpenAI-compatible server (vLLM, TGI, etc.) — LiteLLM does not append it automatically. + +LAN IPs (e.g., `http://192.168.0.202:8000/v1`) are reachable from inside the k3d cluster without any additional network configuration. + +#### HTTP Service (in-cluster) + +```bash +obol sell http \ + --upstream \ + --namespace \ + --port \ + --per-request \ + --chain base-sepolia \ + --health-path \ + --register \ + --register-name "" \ + --register-description "" \ + --register-skills \ + --register-domains +``` + +### Phase 5: Wait for Reconciliation + +The agent reconciler automatically processes the ServiceOffer through 6 stages. + +```bash +# Watch the conditions progress (check every 15 seconds, up to 2 minutes) +for i in $(seq 1 8); do + echo "--- Attempt $i ---" + obol sell status -n 2>&1 | grep -A1 'type:\|status:\|reason:\|message:' + sleep 15 +done +``` + +Expected progression: +1. ModelReady → True (instant for HTTP, may take minutes if pulling a model) +2. UpstreamHealthy → True +3. PaymentGateReady → True +4. RoutePublished → True +5. Registered → True (best-effort, non-blocking) +6. Ready → True + +If a stage is stuck, check: +```bash +# Agent logs for reconciliation errors +obol kubectl logs -l app=openclaw -n openclaw-obol-agent --tail=50 + +# x402-verifier is running +obol kubectl get pods -n x402 +``` + +### Phase 6: Verify the Endpoint + +```bash +# Get the tunnel URL +obol tunnel status + +# Probe the endpoint (should return 402 with pricing) +obol sell probe -n + +# Or manually: +TUNNEL_URL=$(obol tunnel status 2>&1 | grep -o 'https://[^ ]*') +curl -s -o /dev/null -w "%{http_code}" "$TUNNEL_URL/services//health" +# Expected: 402 + +# Inspect the 402 pricing response +curl -s "$TUNNEL_URL/services//health" | python3 -m json.tool +``` + +A 402 response with `x402Version: 1` and an `accepts` array confirms the endpoint is live and payment-gated. + +### Phase 7: Report to User + +Present a summary: + +``` +Service monetized successfully! + + Name: + Model: (if inference) + Price: USDC per request + Chain: base-sepolia + Wallet: + Endpoint: /services//v1/chat/completions + Registry: Agent # on ERC-8004 (Base Sepolia) + +Buyers can discover this service at: + /.well-known/agent-registration.json + +To check status: obol sell status -n +To stop selling: obol sell stop -n +To delete: obol sell delete -n +``` + +## OASF Skills & Domains Reference + +Use these when registering for on-chain discovery: + +**Common skills**: +- `natural_language_processing/natural_language_generation/text_completion` — LLM chat +- `natural_language_processing/text_generation` — general text generation +- `data_management/indexing` — data indexing services +- `data_management/search` — search services +- `devops_mlops/model_versioning` — training/fine-tuning + +**Common domains**: +- `technology/data_science` — AI services +- `research_and_development/scientific_research` — ML research +- `technology/blockchain` — blockchain data services + +## Constraints + +- **Always confirm pricing with the user** — never set a price autonomously +- **Do NOT run `obol stack up`** without explicit user request — it's a long-running infra change +- **Do NOT run `obol stack down` or `obol stack purge`** — destructive operations +- **Registration is best-effort** — services become Ready even if on-chain mint fails (wallet may lack gas funds) +- **Tunnel URL changes on restart** — registration docs auto-update on next reconcile diff --git a/internal/embed/skills/monetize-guide/references/seller-prompt.md b/internal/embed/skills/monetize-guide/references/seller-prompt.md new file mode 100644 index 00000000..9d58a8dc --- /dev/null +++ b/internal/embed/skills/monetize-guide/references/seller-prompt.md @@ -0,0 +1,102 @@ +# Seller Onboarding Prompt + +Use this prompt with Claude Code to set up monetization for any service on obol-stack. Copy and adapt it for your specific use case. + +--- + +## Prompt Template + +``` +You are helping me monetize a service on my obol-stack cluster. + +## My Setup + +- Cluster: running (obol stack up completed) +- Service type: [inference / http] +- Service details: [describe what you're selling] + +## What I Need + +1. Check my cluster is ready (nodes, agent RBAC, wallet) +2. Detect available [models / services] and recommend what to sell +3. Research comparable pricing on the ERC-8004 registry +4. Propose a price and ask me to confirm +5. Create the ServiceOffer and wait for it to reach Ready +6. Verify the endpoint returns a proper 402 to unauthenticated requests +7. Show me the final public URL and how buyers interact with it + +## Constraints + +- Use the `monetize-guide` skill for the end-to-end flow +- Always ask me to confirm pricing before proceeding +- Register the service on ERC-8004 for on-chain discovery +- Use OASF skills/domains that match my service type +``` + +--- + +## Service Type Guidance + +### Inference (Ollama models) + +For monetizing a local LLM: + +- **Upstream**: Ollama at `localhost:11434` (auto-detected) +- **Pricing model**: `--per-request` or `--per-mtok` +- **Registration skills**: `natural_language_processing/natural_language_generation/text_completion` +- **Registration domains**: `technology/data_science` +- **Buyer interaction**: OpenAI-compatible API at `/services//v1/chat/completions` +- **Health check**: Ollama `/api/tags` (auto-configured) + +### HTTP API (generic service) + +For monetizing any HTTP service running in the cluster: + +- **Upstream**: Kubernetes service name + port +- **Pricing model**: `--per-request` +- **Registration skills**: Choose from OASF taxonomy based on what the service does +- **Registration domains**: Choose from OASF taxonomy based on the domain +- **Buyer interaction**: REST API at `/services//*` +- **Health check**: Must have a health endpoint (default: `/health`) + +### Compute (GPU hours) + +For selling GPU compute time (fine-tuning, training): + +- **Upstream**: Worker API (e.g., autoresearch worker at port 8080) +- **Pricing model**: `--per-hour` +- **Registration skills**: `devops_mlops/model_versioning` +- **Registration domains**: `research_and_development/scientific_research` +- **Buyer interaction**: Experiment submission API at `/services//experiment` +- **Health check**: `/health` or `/healthz` +- **Note**: `--per-hour` is approximated to per-request at 5 min/request for x402 gating + +--- + +## Post-Sell Monitoring + +After the service is live, periodically check: + +```bash +# Service status and conditions +obol sell status -n + +# Verify endpoint is payment-gated +obol sell probe -n + +# Check x402 verifier logs for payment activity +obol kubectl logs -l app=x402-verifier -n x402 --tail=20 + +# Check tunnel is active +obol tunnel status +``` + +### Troubleshooting + +| Symptom | Cause | Fix | +|---------|-------|-----| +| ServiceOffer stuck at UpstreamHealthy | Upstream not reachable | Check pod is running, service exists, port is correct | +| ServiceOffer stuck at PaymentGateReady | x402 namespace not ready | Check `obol kubectl get pods -n x402` | +| 404 instead of 402 at endpoint | HTTPRoute not created | Check RoutePublished condition, verify Traefik gateway | +| 200 instead of 402 (no payment gate) | Pricing route missing | Check x402-pricing ConfigMap in x402 namespace | +| Registration failed | Wallet not funded | Non-blocking; service still works without on-chain registration | diff --git a/internal/embed/skills/sell/scripts/monetize.py b/internal/embed/skills/sell/scripts/monetize.py index fd4e5458..8d6f7c77 100644 --- a/internal/embed/skills/sell/scripts/monetize.py +++ b/internal/embed/skills/sell/scripts/monetize.py @@ -203,7 +203,9 @@ def get_effective_price(spec): return price["perRequest"] if price.get("perMTok"): return _approximate_request_price(price["perMTok"]) - return price.get("perHour") or "0" + if price.get("perHour"): + return _approximate_request_price_from_per_hour(price["perHour"]) + return "0" def _approximate_request_price(per_mtok): @@ -215,6 +217,23 @@ def _approximate_request_price(per_mtok): return _decimal_to_string(value / APPROX_TOKENS_PER_REQUEST) +# Autoresearch experiment budget: 5 minutes per experiment. +APPROX_MINUTES_PER_REQUEST = 5 + + +def _approximate_request_price_from_per_hour(per_hour): + """Approximate a per-request price from a per-hour price. + + Uses APPROX_MINUTES_PER_REQUEST (5 min, matching autoresearch budget). + Formula: perRequest = perHour * (minutesPerRequest / 60) + """ + try: + value = Decimal(str(per_hour).strip()) + except InvalidOperation as exc: + raise ValueError(f"invalid perHour price: {per_hour!r}") from exc + return _decimal_to_string(value * APPROX_MINUTES_PER_REQUEST / 60) + + def _decimal_to_string(value): """Format a Decimal without exponent notation or trailing zeros.""" normalized = value.normalize() @@ -235,7 +254,10 @@ def describe_price(spec): f"(approx from {price['perMTok']} USDC/MTok @ {int(APPROX_TOKENS_PER_REQUEST)} tok/request)" ) if price.get("perHour"): - return f"{price['perHour']} USDC/hour" + return ( + f"{get_effective_price(spec)} USDC/request " + f"(approx from {price['perHour']} USDC/hour @ {APPROX_MINUTES_PER_REQUEST} min/request)" + ) return "0 USDC/request" @@ -799,6 +821,16 @@ def stage_route_published(spec, ns, name, token, ssl_ctx): port = upstream.get("port", 11434) url_path = spec.get("path", f"/services/{name}") + # Derive the HTTPRoute request timeout from payment.maxTimeoutSeconds. + # GPU workers may need 300s+ for experiments; Traefik's default is 30s. + # Add 120s overhead for facilitator verification + network latency. + payment = spec.get("payment", {}) + try: + max_timeout = int(payment.get("maxTimeoutSeconds", 0) or 0) + except (ValueError, TypeError): + max_timeout = 0 + route_timeout_seconds = max(max_timeout + 120, 60) if max_timeout > 30 else 0 + # Build the HTTPRoute resource. # Use ExtensionRef filter (not traefik.io/middleware annotation) because # Traefik's Gateway API provider ignores annotations — only ExtensionRef @@ -869,6 +901,14 @@ def stage_route_published(spec, ns, name, token, ssl_ctx): }, } + # Set request timeout on the HTTPRoute rule when the upstream may take + # longer than Traefik's default 30s (e.g. GPU experiment workers). + if route_timeout_seconds > 0: + httproute["spec"]["rules"][0]["timeouts"] = { + "request": f"{route_timeout_seconds}s", + } + print(f" HTTPRoute timeout: {route_timeout_seconds}s (from maxTimeoutSeconds={max_timeout})") + # Get UID for OwnerReference. so = api_get( f"/apis/{CRD_GROUP}/{CRD_VERSION}/namespaces/{ns}/{CRD_PLURAL}/{name}", @@ -977,11 +1017,11 @@ def stage_registered(spec, ns, name, token, ssl_ctx): # Set on-chain metadata for indexed discovery (MetadataSet events). # Buyers can filter agents by these keys without fetching every registration JSON. - offer_type = spec.get("type", "http") try: - print(f" Setting on-chain metadata: x402.supported=true, service.type={offer_type}") - _set_metadata_on_chain(agent_id, "x402.supported", b"\x01") - _set_metadata_on_chain(agent_id, "service.type", offer_type.encode("utf-8")) + indexed = build_indexed_metadata(spec) + print(" Setting on-chain metadata for indexed discovery") + for key, value in indexed.items(): + _set_metadata_on_chain(agent_id, key, value) except Exception as e: print(f" Warning: on-chain metadata failed (non-blocking): {e}") @@ -990,30 +1030,42 @@ def stage_registered(spec, ns, name, token, ssl_ctx): return True -def _publish_registration_json(spec, ns, name, agent_id, tx_hash, token, ssl_ctx): - """Publish the ERC-8004 AgentRegistration document. +def _coerce_registration_metadata(registration): + """Return registration metadata as a string-key/string-value dict.""" + raw = registration.get("metadata", {}) if isinstance(registration, dict) else {} + if not isinstance(raw, dict): + return {} + meta = {} + for key, value in raw.items(): + if key is None: + continue + key_text = str(key).strip() + if not key_text: + continue + if value is None: + continue + meta[key_text] = str(value) + return meta - Creates four agent-managed resources (all with ownerReferences for GC): - 1. ConfigMap so--registration — the JSON document - 2. Deployment so--registration — busybox httpd serving the ConfigMap - 3. Service so--registration — ClusterIP targeting the deployment - 4. HTTPRoute so--wellknown — routes /.well-known/agent-registration.json - On ServiceOffer deletion, K8s garbage collection tears everything down. +def build_indexed_metadata(spec): + """Build metadata entries for indexed on-chain discovery.""" + offer_type = spec.get("type", "http") + registration = spec.get("registration", {}) + entries = { + "x402.supported": b"\x01", + "service.type": str(offer_type).encode("utf-8"), + } + for key, value in _coerce_registration_metadata(registration).items(): + entries[f"metadata.{key}"] = value.encode("utf-8") + return entries - NOTE: ERC-8004 allows multiple services in a single registration.json. - Currently each ServiceOffer creates its own registration document. When - multiple offers share one agent identity, this should evolve to aggregate - all offers' services into a single /.well-known/agent-registration.json. - """ + +def build_registration_doc(spec, name, agent_id, base_url): + """Build the ERC-8004 registration document for a ServiceOffer.""" registration = spec.get("registration", {}) - base_url = os.environ.get("AGENT_BASE_URL", "http://obol.stack:8080") url_path = spec.get("path", f"/services/{name}") - # ── 1. Build the registration JSON ───────────────────────────────────── - # ERC-8004 REQUIRED fields: type, name, description, image, services, - # x402Support, active, registrations. All are always emitted. - # Build a richer description from spec fields. offer_type = spec.get("type", "http") price_info = get_effective_price(spec) model_info = spec.get("model", {}) @@ -1021,13 +1073,11 @@ def _publish_registration_json(spec, ns, name, agent_id, tx_hash, token, ssl_ctx if model_info.get("name"): default_desc = f"{model_info['name']} inference via x402 micropayments ({price_info} USDC/request)" - # OASF skills and domains for machine-readable capability discovery. - # Defaults based on service type; overridden by spec.registration.skills/domains. default_skills = { - "inference": ["natural_language_processing/text_generation/chat_completion"], + "inference": ["natural_language_processing/natural_language_generation/text_completion"], } default_domains = { - "inference": ["technology/artificial_intelligence"], + "inference": ["technology/data_science"], } skills = registration.get("skills", default_skills.get(offer_type, [])) domains = registration.get("domains", default_domains.get(offer_type, [])) @@ -1054,7 +1104,6 @@ def _publish_registration_json(spec, ns, name, agent_id, tx_hash, token, ssl_ctx "supportedTrust": registration.get("supportedTrust", []), } - # Add OASF service entry for structured capability discovery. if skills or domains: oasf_entry = {"name": "OASF", "version": "0.8"} if skills: @@ -1068,6 +1117,37 @@ def _publish_registration_json(spec, ns, name, agent_id, tx_hash, token, ssl_ctx if svc.get("endpoint"): doc["services"].append(svc) + metadata = _coerce_registration_metadata(registration) + if metadata: + doc["metadata"] = metadata + + provenance = spec.get("provenance") + if provenance: + doc["provenance"] = {k: v for k, v in provenance.items() if v} + + return doc + + +def _publish_registration_json(spec, ns, name, agent_id, tx_hash, token, ssl_ctx): + """Publish the ERC-8004 AgentRegistration document. + + Creates four agent-managed resources (all with ownerReferences for GC): + 1. ConfigMap so--registration — the JSON document + 2. Deployment so--registration — busybox httpd serving the ConfigMap + 3. Service so--registration — ClusterIP targeting the deployment + 4. HTTPRoute so--wellknown — routes /.well-known/agent-registration.json + + On ServiceOffer deletion, K8s garbage collection tears everything down. + + NOTE: ERC-8004 allows multiple services in a single registration.json. + Currently each ServiceOffer creates its own registration document. When + multiple offers share one agent identity, this should evolve to aggregate + all offers' services into a single /.well-known/agent-registration.json. + """ + base_url = os.environ.get("AGENT_BASE_URL", "http://obol.stack:8080") + + # ── 1. Build the registration JSON ───────────────────────────────────── + doc = build_registration_doc(spec, name, agent_id, base_url) doc_json = json.dumps(doc, indent=2) # ── Get ServiceOffer UID for ownerReferences ─────────────────────────── @@ -1225,9 +1305,29 @@ def _publish_registration_json(spec, ns, name, agent_id, tx_hash, token, ssl_ctx print(f" Published registration at /.well-known/agent-registration.json") -# --------------------------------------------------------------------------- -# /skill.md — aggregate agent-optimized service catalog -# --------------------------------------------------------------------------- +def _apply_resource(collection_path, name, resource, token, ssl_ctx): + """Create-or-update a Kubernetes resource (idempotent). + + Uses a direct HTTP GET to distinguish 404 (create) from other errors + (permission denied, server error) rather than catching SystemExit from + api_get which treats all failures as 404. + """ + api_server = os.environ.get("KUBERNETES_SERVICE_HOST", "kubernetes.default.svc") + api_port = os.environ.get("KUBERNETES_SERVICE_PORT", "443") + url = f"https://{api_server}:{api_port}{collection_path}/{name}" + req = urllib.request.Request(url, headers={"Authorization": f"Bearer {token}"}) + try: + urllib.request.urlopen(req, context=ssl_ctx, timeout=15) + # Exists — patch it. + api_patch(f"{collection_path}/{name}", resource, token, ssl_ctx, patch_type="merge") + except urllib.error.HTTPError as e: + if e.code == 404: + api_post(collection_path, resource, token, ssl_ctx) + else: + body = e.read().decode() if e.fp else "" + print(f" Failed to check {name}: HTTP {e.code}: {body[:200]}", file=sys.stderr) + raise RuntimeError(f"K8s API error {e.code} for {name}") from e + def _build_skill_md(items, base_url): """Build /skill.md content from all Ready ServiceOffer items.""" @@ -1237,7 +1337,6 @@ def _build_skill_md(items, base_url): if is_condition_true(conditions, "Ready"): ready.append(item) - # Resolve agent name from the first offer's registration, or fallback. agent_name = "Obol Stack" if ready: reg = ready[0].get("spec", {}).get("registration", {}) @@ -1257,7 +1356,6 @@ def _build_skill_md(items, base_url): lines.append("**No services currently available.**\n") return "\n".join(lines) - # ── Summary table ───────────────────────────────────────────────────── lines.append("## Services\n") lines.append("| Service | Type | Model | Price | Endpoint |") lines.append("|---------|------|-------|-------|----------|") @@ -1271,7 +1369,6 @@ def _build_skill_md(items, base_url): lines.append(f"| [{name}](#{name}) | {offer_type} | {model_name} | {price_desc} | `{base_url}{path}` |") lines.append("") - # ── How to pay ──────────────────────────────────────────────────────── lines.append("## How to Pay (x402 Protocol)\n") lines.append("1. **Send a normal HTTP request** to the service endpoint") lines.append("2. **Receive HTTP 402** with `X-Payment` response header containing JSON pricing:") @@ -1303,7 +1400,6 @@ def _build_skill_md(items, base_url): lines.append("For programmatic payment, use [x402-go](https://github.com/coinbase/x402/tree/main/go), [x402-js](https://github.com/coinbase/x402/tree/main/typescript), or sign ERC-3009 directly with ethers/viem/web3.py.") lines.append("") - # ── Per-service details ─────────────────────────────────────────────── lines.append("## Service Details\n") for item in ready: spec = item.get("spec", {}) @@ -1335,7 +1431,6 @@ def _build_skill_md(items, base_url): lines.append("```") lines.append("") - # ── Reference ───────────────────────────────────────────────────────── lines.append("## USDC Contract Addresses\n") lines.append("| Network | Address |") lines.append("|---------|---------|") @@ -1374,7 +1469,6 @@ def _publish_skill_md(items, token, ssl_ctx): route_name = "obol-skill-md-route" labels = {"app": deploy_name, "obol.org/managed-by": "monetize"} - # ── 1. ConfigMap ────────────────────────────────────────────────────── configmap = { "apiVersion": "v1", "kind": "ConfigMap", @@ -1386,7 +1480,6 @@ def _publish_skill_md(items, token, ssl_ctx): } _apply_resource(f"/api/v1/namespaces/{agent_ns}/configmaps", cm_name, configmap, token, ssl_ctx) - # ── 2. Deployment (busybox httpd) ───────────────────────────────────── deployment = { "apiVersion": "apps/v1", "kind": "Deployment", @@ -1438,7 +1531,6 @@ def _publish_skill_md(items, token, ssl_ctx): } _apply_resource(f"/apis/apps/v1/namespaces/{agent_ns}/deployments", deploy_name, deployment, token, ssl_ctx) - # ── 3. Service ──────────────────────────────────────────────────────── service = { "apiVersion": "v1", "kind": "Service", @@ -1451,7 +1543,6 @@ def _publish_skill_md(items, token, ssl_ctx): } _apply_resource(f"/api/v1/namespaces/{agent_ns}/services", svc_name, service, token, ssl_ctx) - # ── 4. HTTPRoute (public, no ForwardAuth) ───────────────────────────── httproute = { "apiVersion": "gateway.networking.k8s.io/v1", "kind": "HTTPRoute", @@ -1477,30 +1568,6 @@ def _publish_skill_md(items, token, ssl_ctx): print(f" Published /skill.md ({ready_count} service(s))") -def _apply_resource(collection_path, name, resource, token, ssl_ctx): - """Create-or-update a Kubernetes resource (idempotent). - - Uses a direct HTTP GET to distinguish 404 (create) from other errors - (permission denied, server error) rather than catching SystemExit from - api_get which treats all failures as 404. - """ - api_server = os.environ.get("KUBERNETES_SERVICE_HOST", "kubernetes.default.svc") - api_port = os.environ.get("KUBERNETES_SERVICE_PORT", "443") - url = f"https://{api_server}:{api_port}{collection_path}/{name}" - req = urllib.request.Request(url, headers={"Authorization": f"Bearer {token}"}) - try: - urllib.request.urlopen(req, context=ssl_ctx, timeout=15) - # Exists — patch it. - api_patch(f"{collection_path}/{name}", resource, token, ssl_ctx, patch_type="merge") - except urllib.error.HTTPError as e: - if e.code == 404: - api_post(collection_path, resource, token, ssl_ctx) - else: - body = e.read().decode() if e.fp else "" - print(f" Failed to check {name}: HTTP {e.code}: {body[:200]}", file=sys.stderr) - raise RuntimeError(f"K8s API error {e.code} for {name}") from e - - def reconcile(ns, name, token, ssl_ctx): """Reconcile a single ServiceOffer through all stages.""" path = f"/apis/{CRD_GROUP}/{CRD_VERSION}/namespaces/{ns}/{CRD_PLURAL}/{name}" diff --git a/internal/inference/store.go b/internal/inference/store.go index 46d38673..e3a27a7c 100644 --- a/internal/inference/store.go +++ b/internal/inference/store.go @@ -86,9 +86,14 @@ type Deployment struct { ModelHash string `json:"model_hash,omitempty"` // NoPaymentGate disables the built-in x402 payment middleware when the - // gateway is routed through the cluster's x402 verifier via Traefik. + // gateway runs behind the cluster's x402 verifier to avoid double-gating. NoPaymentGate bool `json:"no_payment_gate,omitempty"` + // Provenance holds optional metadata about how the model was produced + // (e.g. autoresearch experiment results). Stored alongside the deployment + // config and passed to the registration document when selling. + Provenance *Provenance `json:"provenance,omitempty"` + // CreatedAt is the RFC3339 timestamp of when this deployment was created. CreatedAt string `json:"created_at"` @@ -96,6 +101,18 @@ type Deployment struct { UpdatedAt string `json:"updated_at,omitempty"` } +// Provenance tracks how a model or service was produced. +// JSON field names use camelCase so the same document can flow through +// publish.py -> --provenance-file -> ServiceOffer -> agent-registration.json. +type Provenance struct { + Framework string `json:"framework,omitempty"` // e.g. "autoresearch" + MetricName string `json:"metricName,omitempty"` // e.g. "val_bpb" + MetricValue string `json:"metricValue,omitempty"` // e.g. "0.9973" + ExperimentID string `json:"experimentId,omitempty"` // commit hash or UUID + TrainHash string `json:"trainHash,omitempty"` // e.g. "sha256:..." + ParamCount string `json:"paramCount,omitempty"` // e.g. "50000000" +} + // validDeploymentName matches safe deployment names: alphanumeric, hyphens, // underscores, 1-63 chars. No path separators, dots, or shell metacharacters. var validDeploymentName = regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9_-]{0,62}$`) diff --git a/internal/inference/store_test.go b/internal/inference/store_test.go index 7022b0e9..9771c657 100644 --- a/internal/inference/store_test.go +++ b/internal/inference/store_test.go @@ -78,6 +78,54 @@ func TestStoreCreate_PersistsPerMTokMetadata(t *testing.T) { } } +func TestStoreCreate_PersistsCanonicalProvenance(t *testing.T) { + dir := t.TempDir() + store := inference.NewStore(dir) + + d := &inference.Deployment{ + Name: "prov", + WalletAddress: "0xdeadbeef", + Provenance: &inference.Provenance{ + Framework: "autoresearch", + MetricName: "val_bpb", + MetricValue: "0.9973", + ExperimentID: "abc123", + TrainHash: "sha256:deadbeef", + ParamCount: "50000000", + }, + } + + if err := store.Create(d, false); err != nil { + t.Fatalf("Create: %v", err) + } + + got, err := store.Get("prov") + if err != nil { + t.Fatalf("Get: %v", err) + } + if got.Provenance == nil { + t.Fatal("Provenance should be persisted") + } + if got.Provenance.Framework != "autoresearch" { + t.Errorf("Framework = %q, want %q", got.Provenance.Framework, "autoresearch") + } + if got.Provenance.MetricName != "val_bpb" { + t.Errorf("MetricName = %q, want %q", got.Provenance.MetricName, "val_bpb") + } + if got.Provenance.MetricValue != "0.9973" { + t.Errorf("MetricValue = %q, want %q", got.Provenance.MetricValue, "0.9973") + } + if got.Provenance.ExperimentID != "abc123" { + t.Errorf("ExperimentID = %q, want %q", got.Provenance.ExperimentID, "abc123") + } + if got.Provenance.TrainHash != "sha256:deadbeef" { + t.Errorf("TrainHash = %q, want %q", got.Provenance.TrainHash, "sha256:deadbeef") + } + if got.Provenance.ParamCount != "50000000" { + t.Errorf("ParamCount = %q, want %q", got.Provenance.ParamCount, "50000000") + } +} + func TestStoreCreateDuplicate(t *testing.T) { dir := t.TempDir() store := inference.NewStore(dir) diff --git a/internal/network/validate.go b/internal/network/validate.go new file mode 100644 index 00000000..adc96e9b --- /dev/null +++ b/internal/network/validate.go @@ -0,0 +1,62 @@ +package network + +import ( + "fmt" + "strconv" + "strings" +) + +func validateInstallOptions(networkName string, values map[string]string) error { + if networkName != "ethereum" { + return nil + } + + rethIndexerEnabled, err := parseBoolString(values["RethIndexerEnabled"]) + if err != nil { + return fmt.Errorf("invalid --reth-indexer-enabled value %q: %w", values["RethIndexerEnabled"], err) + } + if !rethIndexerEnabled { + return nil + } + + if values["ExecutionClient"] != "reth" { + return fmt.Errorf("the embedded ERC-8004 indexer requires --execution-client reth") + } + if strings.TrimSpace(values["RethImageRepository"]) == "" || strings.TrimSpace(values["RethImageTag"]) == "" { + return fmt.Errorf("the embedded ERC-8004 indexer requires both --reth-image-repository and --reth-image-tag") + } + + port := strings.TrimSpace(values["RethIndexerPort"]) + if port != "" { + numericPort, err := strconv.Atoi(port) + if err != nil || numericPort <= 0 || numericPort > 65535 { + return fmt.Errorf("invalid --reth-indexer-port value %q", values["RethIndexerPort"]) + } + } + + if strings.TrimSpace(values["RethIndexerDbPath"]) == "" { + return fmt.Errorf("the embedded ERC-8004 indexer requires --reth-indexer-db-path") + } + if strings.TrimSpace(values["RethIndexerRegistryAddress"]) == "" { + return fmt.Errorf("the embedded ERC-8004 indexer requires --reth-indexer-registry-address") + } + + if backfill := strings.TrimSpace(values["RethIndexerBackfillFromBlock"]); backfill != "" { + if _, err := strconv.ParseUint(backfill, 10, 64); err != nil { + return fmt.Errorf("invalid --reth-indexer-backfill-from-block value %q", values["RethIndexerBackfillFromBlock"]) + } + } + + return nil +} + +func parseBoolString(value string) (bool, error) { + switch strings.TrimSpace(strings.ToLower(value)) { + case "", "false", "0", "no": + return false, nil + case "true", "1", "yes": + return true, nil + default: + return false, fmt.Errorf("expected true/false") + } +} diff --git a/internal/network/validate_test.go b/internal/network/validate_test.go new file mode 100644 index 00000000..c6600952 --- /dev/null +++ b/internal/network/validate_test.go @@ -0,0 +1,71 @@ +package network + +import "testing" + +func TestValidateInstallOptions_AllowsDisabledIndexer(t *testing.T) { + values := map[string]string{ + "ExecutionClient": "geth", + "RethIndexerEnabled": "false", + "RethImageRepository": "", + "RethImageTag": "", + "RethIndexerPort": "8088", + "RethIndexerDbPath": "/data/erc8004-indexer/indexer.db", + "RethIndexerRegistryAddress": "0x8004A818BFB912233c491871b3d84c89A494BD9e", + "RethIndexerBackfillFromBlock": "0", + } + + if err := validateInstallOptions("ethereum", values); err != nil { + t.Fatalf("validateInstallOptions returned error for disabled indexer: %v", err) + } +} + +func TestValidateInstallOptions_RequiresRethExecutionClient(t *testing.T) { + values := map[string]string{ + "ExecutionClient": "geth", + "RethIndexerEnabled": "true", + "RethImageRepository": "ghcr.io/example/reth-indexer", + "RethImageTag": "latest", + "RethIndexerPort": "8088", + "RethIndexerDbPath": "/data/erc8004-indexer/indexer.db", + "RethIndexerRegistryAddress": "0x8004A818BFB912233c491871b3d84c89A494BD9e", + "RethIndexerBackfillFromBlock": "0", + } + + if err := validateInstallOptions("ethereum", values); err == nil { + t.Fatal("expected validation error when indexer is enabled on a non-reth client") + } +} + +func TestValidateInstallOptions_RequiresCustomImage(t *testing.T) { + values := map[string]string{ + "ExecutionClient": "reth", + "RethIndexerEnabled": "true", + "RethImageRepository": "", + "RethImageTag": "", + "RethIndexerPort": "8088", + "RethIndexerDbPath": "/data/erc8004-indexer/indexer.db", + "RethIndexerRegistryAddress": "0x8004A818BFB912233c491871b3d84c89A494BD9e", + "RethIndexerBackfillFromBlock": "0", + } + + if err := validateInstallOptions("ethereum", values); err == nil { + t.Fatal("expected validation error when custom image flags are missing") + } +} + +func TestValidateInstallOptions_RejectsInvalidPort(t *testing.T) { + values := map[string]string{ + "ExecutionClient": "reth", + "RethIndexerEnabled": "true", + "RethImageRepository": "ghcr.io/example/reth-indexer", + "RethImageTag": "latest", + "RethIndexerPort": "99999", + "RethIndexerDbPath": "/data/erc8004-indexer/indexer.db", + "RethIndexerRegistryAddress": "0x8004A818BFB912233c491871b3d84c89A494BD9e", + "RethIndexerBackfillFromBlock": "0", + } + + if err := validateInstallOptions("ethereum", values); err == nil { + t.Fatal("expected validation error for invalid indexer port") + } +} diff --git a/internal/openclaw/openclaw.go b/internal/openclaw/openclaw.go index d1d105c9..7147130e 100644 --- a/internal/openclaw/openclaw.go +++ b/internal/openclaw/openclaw.go @@ -2268,9 +2268,6 @@ func promptForDirectProvider(reader *bufio.Reader, providerName, display, defaul if baseURL == "" { baseURL = defaultBaseURL } - // Strip trailing /v1 — LiteLLM auto-appends it for OpenAI-compatible providers. - baseURL = model.WarnAndStripV1Suffix(baseURL) - return buildDirectProviderOverlay(providerName, baseURL, defaultAPI, defaultAPIKeyEnvVar, modelID, modelName, apiKey), nil } @@ -2282,9 +2279,6 @@ func promptForCustomProvider(reader *bufio.Reader) (*ImportResult, error) { if baseURL == "" { return nil, fmt.Errorf("custom base URL is required") } - // Strip trailing /v1 — LiteLLM auto-appends it for OpenAI-compatible providers. - baseURL = model.WarnAndStripV1Suffix(baseURL) - fmt.Printf("Custom model ID: ") modelID, _ := reader.ReadString('\n') modelID = strings.TrimSpace(modelID) diff --git a/internal/schemas/payment.go b/internal/schemas/payment.go index fde63dfd..a9aa5b8c 100644 --- a/internal/schemas/payment.go +++ b/internal/schemas/payment.go @@ -18,7 +18,14 @@ import ( // convert per-million-token pricing into an x402 per-request charge. const ApproxTokensPerRequest = 1000 +// ApproxMinutesPerRequest is the temporary phase-1 time estimate used to +// convert per-hour pricing into an x402 per-request charge. Based on the +// autoresearch 5-minute experiment budget. +const ApproxMinutesPerRequest = 5 + var approxTokensPerRequestDecimal = decimal.NewFromInt(ApproxTokensPerRequest) +var minutesPerHour = decimal.NewFromInt(60) +var approxMinutesPerRequestDecimal = decimal.NewFromInt(ApproxMinutesPerRequest) // PaymentTerms defines x402 payment requirements for a ServiceOffer. // Field names align with x402 PaymentRequirements (V2). @@ -84,7 +91,7 @@ func (p PriceTable) EffectiveRequestPriceE() (string, error) { return ApproximateRequestPriceFromPerMTok(p.PerMTok) } if p.PerHour != "" { - return p.PerHour, nil + return ApproximateRequestPriceFromPerHour(p.PerHour) } return "0", nil } @@ -98,3 +105,15 @@ func ApproximateRequestPriceFromPerMTok(perMTok string) (string, error) { } return value.Div(approxTokensPerRequestDecimal).String(), nil } + +// ApproximateRequestPriceFromPerHour converts a per-hour price into a temporary +// per-request x402 charge using ApproxMinutesPerRequest (default 5 min, +// matching the autoresearch experiment budget). +// Formula: perRequest = perHour * (minutesPerRequest / 60) +func ApproximateRequestPriceFromPerHour(perHour string) (string, error) { + value, err := decimal.NewFromString(strings.TrimSpace(perHour)) + if err != nil { + return "", err + } + return value.Mul(approxMinutesPerRequestDecimal).Div(minutesPerHour).String(), nil +} diff --git a/internal/schemas/payment_test.go b/internal/schemas/payment_test.go index 9233b19f..39e72dd1 100644 --- a/internal/schemas/payment_test.go +++ b/internal/schemas/payment_test.go @@ -22,9 +22,36 @@ func TestEffectiveRequestPrice_PerMTok(t *testing.T) { } func TestEffectiveRequestPrice_PerHour(t *testing.T) { - p := PriceTable{PerHour: "2.00"} - if got := p.EffectiveRequestPrice(); got != "2.00" { - t.Errorf("EffectiveRequestPrice() = %q, want %q", got, "2.00") + // 6.00 USDC/hour * (5 min / 60 min) = 0.50 USDC/request + p := PriceTable{PerHour: "6.00"} + if got := p.EffectiveRequestPrice(); got != "0.5" { + t.Errorf("EffectiveRequestPrice() = %q, want %q", got, "0.5") + } +} + +func TestApproximateRequestPriceFromPerHour(t *testing.T) { + // 0.50 USDC/hour * (5/60) = 0.04166... + got, err := ApproximateRequestPriceFromPerHour("0.50") + if err != nil { + t.Fatalf("ApproximateRequestPriceFromPerHour() error = %v", err) + } + // 6.00 USDC/hour gives exactly 0.5, so use that for an exact check too. + got6, err := ApproximateRequestPriceFromPerHour("6.00") + if err != nil { + t.Fatalf("ApproximateRequestPriceFromPerHour(6.00) error = %v", err) + } + if got6 != "0.5" { + t.Errorf("ApproximateRequestPriceFromPerHour(6.00) = %q, want %q", got6, "0.5") + } + // 0.50 * 5/60 should start with "0.0416" + if len(got) < 6 || got[:6] != "0.0416" { + t.Errorf("ApproximateRequestPriceFromPerHour(0.50) = %q, expected value starting with 0.0416", got) + } +} + +func TestApproximateRequestPriceFromPerHour_Invalid(t *testing.T) { + if _, err := ApproximateRequestPriceFromPerHour("bad"); err == nil { + t.Fatal("ApproximateRequestPriceFromPerHour() error = nil, want non-nil") } } diff --git a/internal/tunnel/tunnel_lifecycle_test.go b/internal/tunnel/tunnel_lifecycle_test.go new file mode 100644 index 00000000..fc0cf61e --- /dev/null +++ b/internal/tunnel/tunnel_lifecycle_test.go @@ -0,0 +1,294 @@ +package tunnel + +import ( + "os" + "path/filepath" + "testing" + "time" + + "github.com/ObolNetwork/obol-stack/internal/config" +) + +func testConfig(t *testing.T) *config.Config { + t.Helper() + dir := t.TempDir() + return &config.Config{ + ConfigDir: filepath.Join(dir, "config"), + DataDir: filepath.Join(dir, "data"), + BinDir: filepath.Join(dir, "bin"), + } +} + +// --------------------------------------------------------------------------- +// State persistence +// --------------------------------------------------------------------------- + +func TestTunnelState_RoundTrip(t *testing.T) { + cfg := testConfig(t) + + st := &tunnelState{ + Mode: "quick", + Hostname: "abc-def.trycloudflare.com", + } + + if err := saveTunnelState(cfg, st); err != nil { + t.Fatalf("save: %v", err) + } + + got, err := loadTunnelState(cfg) + if err != nil { + t.Fatalf("load: %v", err) + } + if got.Mode != "quick" { + t.Errorf("mode = %q, want quick", got.Mode) + } + if got.Hostname != "abc-def.trycloudflare.com" { + t.Errorf("hostname = %q, want abc-def.trycloudflare.com", got.Hostname) + } + if got.UpdatedAt.IsZero() { + t.Error("UpdatedAt should be set by save") + } +} + +func TestTunnelState_DNSMode(t *testing.T) { + cfg := testConfig(t) + + st := &tunnelState{ + Mode: "dns", + Hostname: "stack.example.com", + AccountID: "acct-123", + ZoneID: "zone-456", + TunnelID: "tun-789", + TunnelName: "my-tunnel", + } + + if err := saveTunnelState(cfg, st); err != nil { + t.Fatalf("save: %v", err) + } + + got, err := loadTunnelState(cfg) + if err != nil { + t.Fatalf("load: %v", err) + } + if got.Mode != "dns" { + t.Errorf("mode = %q, want dns", got.Mode) + } + if got.TunnelID != "tun-789" { + t.Errorf("tunnel_id = %q, want tun-789", got.TunnelID) + } +} + +func TestTunnelState_NotExist(t *testing.T) { + cfg := testConfig(t) + + got, err := loadTunnelState(cfg) + if err != nil { + t.Fatalf("load should not error on missing file: %v", err) + } + if got != nil { + t.Fatalf("expected nil state for missing file, got %+v", got) + } +} + +func TestTunnelState_Overwrite(t *testing.T) { + cfg := testConfig(t) + + st1 := &tunnelState{Mode: "quick", Hostname: "old.trycloudflare.com"} + if err := saveTunnelState(cfg, st1); err != nil { + t.Fatalf("save 1: %v", err) + } + + st2 := &tunnelState{Mode: "dns", Hostname: "new.example.com"} + if err := saveTunnelState(cfg, st2); err != nil { + t.Fatalf("save 2: %v", err) + } + + got, err := loadTunnelState(cfg) + if err != nil { + t.Fatalf("load: %v", err) + } + if got.Mode != "dns" || got.Hostname != "new.example.com" { + t.Errorf("expected overwritten state, got %+v", got) + } +} + +func TestTunnelState_FilePermissions(t *testing.T) { + cfg := testConfig(t) + + st := &tunnelState{Mode: "quick", Hostname: "test.trycloudflare.com"} + if err := saveTunnelState(cfg, st); err != nil { + t.Fatalf("save: %v", err) + } + + info, err := os.Stat(tunnelStatePath(cfg)) + if err != nil { + t.Fatalf("stat: %v", err) + } + perm := info.Mode().Perm() + if perm != 0600 { + t.Errorf("file permissions = %o, want 0600", perm) + } +} + +// --------------------------------------------------------------------------- +// Mode and URL derivation +// --------------------------------------------------------------------------- + +func TestTunnelModeAndURL(t *testing.T) { + tests := []struct { + name string + st *tunnelState + wantMode string + wantURL string + }{ + { + name: "nil state", + st: nil, + wantMode: "quick", + wantURL: "", + }, + { + name: "quick mode (no hostname)", + st: &tunnelState{Mode: "quick"}, + wantMode: "quick", + wantURL: "", + }, + { + name: "dns mode with hostname", + st: &tunnelState{Mode: "dns", Hostname: "stack.example.com"}, + wantMode: "dns", + wantURL: "https://stack.example.com", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mode, url := tunnelModeAndURL(tt.st) + if mode != tt.wantMode { + t.Errorf("mode = %q, want %q", mode, tt.wantMode) + } + if url != tt.wantURL { + t.Errorf("url = %q, want %q", url, tt.wantURL) + } + }) + } +} + +// --------------------------------------------------------------------------- +// Auto-stop decision logic +// --------------------------------------------------------------------------- + +// ShouldAutoStopTunnel mirrors the decision logic in cmd/obol/sell.go's +// delete command: stop the quick tunnel only when no ServiceOffers remain +// and the tunnel mode is NOT dns (persistent tunnels shouldn't be auto-stopped). +func shouldAutoStopTunnel(remainingOffers string, st *tunnelState) bool { + // If there are remaining offers, don't stop. + if remainingOffers != "[]" && remainingOffers != "" { + return false + } + // DNS (persistent) tunnels should not be auto-stopped. + if st != nil && st.Mode == "dns" { + return false + } + // Quick tunnels with no remaining offers: stop. + return true +} + +func TestShouldAutoStopTunnel(t *testing.T) { + tests := []struct { + name string + remaining string + state *tunnelState + want bool + }{ + { + name: "offers remain — don't stop", + remaining: `[{"metadata":{"name":"my-offer"}}]`, + state: nil, + want: false, + }, + { + name: "empty list, quick tunnel — stop", + remaining: "[]", + state: &tunnelState{Mode: "quick"}, + want: true, + }, + { + name: "empty list, nil state — stop", + remaining: "[]", + state: nil, + want: true, + }, + { + name: "empty list, dns tunnel — don't stop", + remaining: "[]", + state: &tunnelState{Mode: "dns", Hostname: "stack.example.com"}, + want: false, + }, + { + name: "empty string, quick tunnel — stop", + remaining: "", + state: &tunnelState{Mode: "quick"}, + want: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := shouldAutoStopTunnel(tt.remaining, tt.state) + if got != tt.want { + t.Errorf("shouldAutoStopTunnel(%q, %v) = %v, want %v", tt.remaining, tt.state, got, tt.want) + } + }) + } +} + +// --------------------------------------------------------------------------- +// Exported LoadTunnelState +// --------------------------------------------------------------------------- + +func TestLoadTunnelState_Exported(t *testing.T) { + cfg := testConfig(t) + + // No state file → (nil, nil). + st, err := LoadTunnelState(cfg) + if err != nil || st != nil { + t.Fatalf("expected (nil, nil), got (%v, %v)", st, err) + } + + // Write state, then load via exported function. + if err := saveTunnelState(cfg, &tunnelState{ + Mode: "quick", + Hostname: "exported-test.trycloudflare.com", + }); err != nil { + t.Fatalf("save: %v", err) + } + + st, err = LoadTunnelState(cfg) + if err != nil { + t.Fatalf("LoadTunnelState: %v", err) + } + if st.Hostname != "exported-test.trycloudflare.com" { + t.Errorf("hostname = %q, want exported-test.trycloudflare.com", st.Hostname) + } +} + +// --------------------------------------------------------------------------- +// UpdatedAt timestamp +// --------------------------------------------------------------------------- + +func TestTunnelState_UpdatedAtRefreshed(t *testing.T) { + cfg := testConfig(t) + + before := time.Now().UTC().Add(-time.Second) + + st := &tunnelState{Mode: "quick"} + if err := saveTunnelState(cfg, st); err != nil { + t.Fatalf("save: %v", err) + } + + got, _ := loadTunnelState(cfg) + if got.UpdatedAt.Before(before) { + t.Errorf("UpdatedAt %v should be after %v", got.UpdatedAt, before) + } +} diff --git a/ralph-m1.md b/ralph-m1.md new file mode 100644 index 00000000..41b7ded0 --- /dev/null +++ b/ralph-m1.md @@ -0,0 +1,20 @@ +# Ralph M1: Validate Monetize Inference Selling + +Read this file for full instructions. Load skills: obol-stack-dev, agentic-economy. + +## Gate +Run these flow scripts (REAL user path, not unit tests): +1. `bash flows/flow-06-sell-setup.sh` +2. `bash flows/flow-07-sell-verify.sh` +3. `bash flows/flow-10-anvil-facilitator.sh` +4. `bash flows/flow-08-buy.sh` + +After flow-06: also run `obol sell probe flow-qwen -n llm` + +## Rules +- Use built binary (`obol`), not `go run` +- Wait for real heartbeat (5 min), don't exec monetize.py directly +- Route through Traefik (`obol.stack:8080`), not pod IPs +- Fix failures in Go code OR flow scripts +- Commit with prefix `fix(m1):` +- Write findings to `/tmp/m1-findings.md` diff --git a/ralph-m2.md b/ralph-m2.md new file mode 100644 index 00000000..c05cd03b --- /dev/null +++ b/ralph-m2.md @@ -0,0 +1,23 @@ +# Ralph M2: Validate Autoresearch Worker + Coordinator + +Read /tmp/m1-findings.md for context from M1. +Load skills: obol-stack-dev, agentic-economy. + +## The User Journey +1. Deploy autoresearch-worker as ServiceOffer: + `obol sell http my-worker --upstream worker-api --port 8000 --namespace llm --per-request 0.01 --wallet 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 --chain base-sepolia --register --register-name GPU-Worker` +2. Wait for heartbeat → ServiceOffer Ready +3. Verify 402 at /services/my-worker via curl through Traefik +4. Verify .well-known/agent-registration.json advertises the worker +5. Run discovery.py search → finds registered worker on-chain +6. Run coordinator.py probe → gets x402 pricing from discovered worker + +Use model qwen3.5:9b (NOT qwen3:0.6b). +GPU training not required — validate the HTTP flow and discovery, not training. + +## Rules +- Use built binary (`obol`), not `go run` +- Wait for real heartbeat, don't exec monetize.py directly +- Route through Traefik, not pod IPs +- Commit fixes with prefix `fix(m2):` +- Write findings to `/tmp/m2-findings.md` diff --git a/ralph-m3.md b/ralph-m3.md new file mode 100644 index 00000000..c19ae372 --- /dev/null +++ b/ralph-m3.md @@ -0,0 +1,19 @@ +# Ralph M3: Validate Indexing Serving + +Read /tmp/m1-findings.md and /tmp/m2-findings.md for context. +Load skills: obol-stack-dev. + +## The User Journey +1. Build reth indexer: `cargo build --release` in reth-erc8004-indexer/ +2. Verify 8004scan API: GET /api/v1/public/agents returns agent list +3. Deploy as ServiceOffer: + `obol sell http my-indexer --upstream indexer --port 8080 --namespace llm --per-request 0.0001 --wallet 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 --chain base-sepolia` +4. Verify 402 at /services/my-indexer via Traefik +5. Verify coordinator fallback: internal indexer → external 8004scan + +If reth doesn't build cleanly, document the gap and defer. +Use model qwen3.5:9b. + +## Rules +- Commit fixes with prefix `fix(m3):` +- Write findings to `/tmp/m3-findings.md` diff --git a/reth-erc8004-indexer/.gitignore b/reth-erc8004-indexer/.gitignore new file mode 100644 index 00000000..2f7896d1 --- /dev/null +++ b/reth-erc8004-indexer/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/reth-erc8004-indexer/Cargo.lock b/reth-erc8004-indexer/Cargo.lock new file mode 100644 index 00000000..41edd2f0 --- /dev/null +++ b/reth-erc8004-indexer/Cargo.lock @@ -0,0 +1,11978 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.4", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "aligned-vec" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" +dependencies = [ + "equator", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "alloy-chains" +version = "0.2.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d9d22005bf31b018f31ef9ecadb5d2c39cf4f6acc8db0456f72c815f3d7f757" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "num_enum", + "serde", + "strum", +] + +[[package]] +name = "alloy-consensus" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c0dc44157867da82c469c13186015b86abef209bf0e41625e4b68bac61d728" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "alloy-trie", + "alloy-tx-macros", + "auto_impl", + "borsh", + "c-kzg", + "derive_more", + "either", + "k256", + "once_cell", + "rand 0.8.5", + "secp256k1 0.30.0", + "serde", + "serde_json", + "serde_with", + "thiserror 2.0.18", +] + +[[package]] +name = "alloy-consensus-any" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba4cdb42df3871cd6b346d6a938ec2ba69a9a0f49d1f82714bc5c48349268434" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-dyn-abi" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2db5c583aaef0255aa63a4fe827f826090142528bba48d1bf4119b62780cad" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-type-parser", + "alloy-sol-types", + "derive_more", + "itoa", + "serde", + "serde_json", + "winnow", +] + +[[package]] +name = "alloy-eip2124" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "741bdd7499908b3aa0b159bba11e71c8cddd009a2c2eb7a06e825f1ec87900a5" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "crc", + "serde", + "thiserror 2.0.18", +] + +[[package]] +name = "alloy-eip2930" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9441120fa82df73e8959ae0e4ab8ade03de2aaae61be313fbf5746277847ce25" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "borsh", + "serde", +] + +[[package]] +name = "alloy-eip7702" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2919c5a56a1007492da313e7a3b6d45ef5edc5d33416fdec63c0d7a2702a0d20" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "borsh", + "k256", + "serde", + "serde_with", + "thiserror 2.0.18", +] + +[[package]] +name = "alloy-eip7928" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8222b1d88f9a6d03be84b0f5e76bb60cd83991b43ad8ab6477f0e4a7809b98d" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "borsh", + "serde", +] + +[[package]] +name = "alloy-eips" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f7ef09f21bd1e9cb8a686f168cb4a206646804567f0889eadb8dcc4c9288c8" +dependencies = [ + "alloy-eip2124", + "alloy-eip2930", + "alloy-eip7702", + "alloy-eip7928", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "auto_impl", + "borsh", + "c-kzg", + "derive_more", + "either", + "ethereum_ssz 0.9.1", + "ethereum_ssz_derive 0.9.1", + "serde", + "serde_with", + "sha2", + "thiserror 2.0.18", +] + +[[package]] +name = "alloy-evm" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b991c370ce44e70a3a9e474087e3d65e42e66f967644ad729dc4cec09a21fd09" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-hardforks", + "alloy-primitives", + "alloy-rpc-types-engine", + "alloy-rpc-types-eth", + "alloy-sol-types", + "auto_impl", + "derive_more", + "op-alloy", + "op-revm", + "revm", + "thiserror 2.0.18", +] + +[[package]] +name = "alloy-genesis" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c9cf3b99f46615fbf7dc1add0c96553abb7bf88fc9ec70dfbe7ad0b47ba7fe8" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-serde", + "alloy-trie", + "borsh", + "serde", + "serde_with", +] + +[[package]] +name = "alloy-hardforks" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83ba208044232d14d4adbfa77e57d6329f51bc1acc21f5667bb7db72d88a0831" +dependencies = [ + "alloy-chains", + "alloy-eip2124", + "alloy-primitives", + "auto_impl", + "dyn-clone", + "serde", +] + +[[package]] +name = "alloy-json-abi" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9dbe713da0c737d9e5e387b0ba790eb98b14dd207fe53eef50e19a5a8ec3dac" +dependencies = [ + "alloy-primitives", + "alloy-sol-type-parser", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-json-rpc" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff42cd777eea61f370c0b10f2648a1c81e0b783066cd7269228aa993afd487f7" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "http", + "serde", + "serde_json", + "thiserror 2.0.18", + "tracing", +] + +[[package]] +name = "alloy-network" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cbca04f9b410fdc51aaaf88433cbac761213905a65fe832058bcf6690585762" +dependencies = [ + "alloy-consensus", + "alloy-consensus-any", + "alloy-eips", + "alloy-json-rpc", + "alloy-network-primitives", + "alloy-primitives", + "alloy-rpc-types-any", + "alloy-rpc-types-eth", + "alloy-serde", + "alloy-signer", + "alloy-sol-types", + "async-trait", + "auto_impl", + "derive_more", + "futures-utils-wasm", + "serde", + "serde_json", + "thiserror 2.0.18", +] + +[[package]] +name = "alloy-network-primitives" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d6d15e069a8b11f56bef2eccbad2a873c6dd4d4c81d04dda29710f5ea52f04" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-primitives" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3b431b4e72cd8bd0ec7a50b4be18e73dab74de0dba180eef171055e5d5926e" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "fixed-cache", + "foldhash 0.2.0", + "getrandom 0.4.2", + "hashbrown 0.16.1", + "indexmap 2.13.0", + "itoa", + "k256", + "keccak-asm", + "paste", + "proptest", + "rand 0.9.2", + "rapidhash", + "ruint", + "rustc-hash", + "serde", + "sha3", +] + +[[package]] +name = "alloy-provider" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d181c8cc7cf4805d7e589bf4074d56d55064fa1a979f005a45a62b047616d870" +dependencies = [ + "alloy-chains", + "alloy-consensus", + "alloy-eips", + "alloy-json-rpc", + "alloy-network", + "alloy-network-primitives", + "alloy-primitives", + "alloy-pubsub", + "alloy-rpc-client", + "alloy-rpc-types-debug", + "alloy-rpc-types-eth", + "alloy-rpc-types-trace", + "alloy-signer", + "alloy-sol-types", + "alloy-transport", + "alloy-transport-http", + "alloy-transport-ipc", + "alloy-transport-ws", + "async-stream", + "async-trait", + "auto_impl", + "dashmap", + "either", + "futures", + "futures-utils-wasm", + "lru 0.16.3", + "parking_lot", + "pin-project", + "reqwest", + "serde", + "serde_json", + "thiserror 2.0.18", + "tokio", + "tracing", + "url", + "wasmtimer", +] + +[[package]] +name = "alloy-pubsub" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8bd82953194dec221aa4cbbbb0b1e2df46066fe9d0333ac25b43a311e122d13" +dependencies = [ + "alloy-json-rpc", + "alloy-primitives", + "alloy-transport", + "auto_impl", + "bimap", + "futures", + "parking_lot", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tower", + "tracing", + "wasmtimer", +] + +[[package]] +name = "alloy-rlp" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e93e50f64a77ad9c5470bf2ad0ca02f228da70c792a8f06634801e202579f35e" +dependencies = [ + "alloy-rlp-derive", + "arrayvec", + "bytes", +] + +[[package]] +name = "alloy-rlp-derive" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce8849c74c9ca0f5a03da1c865e3eb6f768df816e67dd3721a398a8a7e398011" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "alloy-rpc-client" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2792758a93ae32a32e9047c843d536e1448044f78422d71bf7d7c05149e103f" +dependencies = [ + "alloy-json-rpc", + "alloy-primitives", + "alloy-pubsub", + "alloy-transport", + "alloy-transport-http", + "alloy-transport-ipc", + "alloy-transport-ws", + "futures", + "pin-project", + "reqwest", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tower", + "tracing", + "url", + "wasmtimer", +] + +[[package]] +name = "alloy-rpc-types" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bdcbf9dfd5eea8bfeb078b1d906da8cd3a39c4d4dbe7a628025648e323611f6" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-engine", + "alloy-rpc-types-eth", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-rpc-types-admin" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42325c117af3a9e49013f881c1474168db57978e02085fc9853a1c89e0562740" +dependencies = [ + "alloy-genesis", + "alloy-primitives", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-rpc-types-anvil" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a3100b76987c1b1dc81f3abe592b7edc29e92b1242067a69d65e0030b35cf9" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-rpc-types-any" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd720b63f82b457610f2eaaf1f32edf44efffe03ae25d537632e7d23e7929e1a" +dependencies = [ + "alloy-consensus-any", + "alloy-rpc-types-eth", + "alloy-serde", +] + +[[package]] +name = "alloy-rpc-types-beacon" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a22e13215866f5dfd5d3278f4c41f1fad9410dc68ce39022f58593c873c26f8" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rpc-types-engine", + "derive_more", + "ethereum_ssz 0.9.1", + "ethereum_ssz_derive 0.9.1", + "serde", + "serde_json", + "serde_with", + "thiserror 2.0.18", + "tree_hash", + "tree_hash_derive", +] + +[[package]] +name = "alloy-rpc-types-debug" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b21e1ad18ff1b31ff1030e046462ab8168cf8894e6778cd805c8bdfe2bd649" +dependencies = [ + "alloy-primitives", + "derive_more", + "serde", + "serde_with", +] + +[[package]] +name = "alloy-rpc-types-engine" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4ac61f03f1edabccde1c687b5b25fff28f183afee64eaa2e767def3929e4457" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "derive_more", + "ethereum_ssz 0.9.1", + "ethereum_ssz_derive 0.9.1", + "jsonwebtoken", + "rand 0.8.5", + "serde", + "strum", +] + +[[package]] +name = "alloy-rpc-types-eth" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2dc411f13092f237d2bf6918caf80977fc2f51485f9b90cb2a2f956912c8c9" +dependencies = [ + "alloy-consensus", + "alloy-consensus-any", + "alloy-eips", + "alloy-network-primitives", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "alloy-sol-types", + "itertools 0.14.0", + "serde", + "serde_json", + "serde_with", + "thiserror 2.0.18", +] + +[[package]] +name = "alloy-rpc-types-mev" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe85bf3be739126aa593dca9fb3ab13ca93fa7873e6f2247be64d7f2cb15f34a" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-rpc-types-trace" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad79f1e27e161943b5a4f99fe5534ef0849876214be411e0032c12f38e94daa" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "serde", + "serde_json", + "thiserror 2.0.18", +] + +[[package]] +name = "alloy-rpc-types-txpool" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d459f902a2313737bc66d18ed094c25d2aeb268b74d98c26bbbda2aa44182ab0" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-serde" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2ce1e0dbf7720eee747700e300c99aac01b1a95bb93f493a01e78ee28bb1a37" +dependencies = [ + "alloy-primitives", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-signer" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2425c6f314522c78e8198979c8cbf6769362be4da381d4152ea8eefce383535d" +dependencies = [ + "alloy-primitives", + "async-trait", + "auto_impl", + "either", + "elliptic-curve", + "k256", + "thiserror 2.0.18", +] + +[[package]] +name = "alloy-signer-local" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ecb71ee53d8d9c3fa7bac17542c8116ebc7a9726c91b1bf333ec3d04f5a789" +dependencies = [ + "alloy-consensus", + "alloy-network", + "alloy-primitives", + "alloy-signer", + "async-trait", + "coins-bip32", + "coins-bip39", + "k256", + "rand 0.8.5", + "thiserror 2.0.18", + "zeroize", +] + +[[package]] +name = "alloy-sol-macro" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab81bab693da9bb79f7a95b64b394718259fdd7e41dceeced4cad57cb71c4f6a" +dependencies = [ + "alloy-sol-macro-expander", + "alloy-sol-macro-input", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "alloy-sol-macro-expander" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "489f1620bb7e2483fb5819ed01ab6edc1d2f93939dce35a5695085a1afd1d699" +dependencies = [ + "alloy-sol-macro-input", + "const-hex", + "heck", + "indexmap 2.13.0", + "proc-macro-error2", + "proc-macro2", + "quote", + "sha3", + "syn 2.0.117", + "syn-solidity", +] + +[[package]] +name = "alloy-sol-macro-input" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56cef806ad22d4392c5fc83cf8f2089f988eb99c7067b4e0c6f1971fc1cca318" +dependencies = [ + "const-hex", + "dunce", + "heck", + "macro-string", + "proc-macro2", + "quote", + "syn 2.0.117", + "syn-solidity", +] + +[[package]] +name = "alloy-sol-type-parser" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6df77fea9d6a2a75c0ef8d2acbdfd92286cc599983d3175ccdc170d3433d249" +dependencies = [ + "serde", + "winnow", +] + +[[package]] +name = "alloy-sol-types" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64612d29379782a5dde6f4b6570d9c756d734d760c0c94c254d361e678a6591f" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-macro", + "serde", +] + +[[package]] +name = "alloy-transport" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa186e560d523d196580c48bf00f1bf62e63041f28ecf276acc22f8b27bb9f53" +dependencies = [ + "alloy-json-rpc", + "auto_impl", + "base64 0.22.1", + "derive_more", + "futures", + "futures-utils-wasm", + "parking_lot", + "serde", + "serde_json", + "thiserror 2.0.18", + "tokio", + "tower", + "tracing", + "url", + "wasmtimer", +] + +[[package]] +name = "alloy-transport-http" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa501ad58dd20acddbfebc65b52e60f05ebf97c52fa40d1b35e91f5e2da0ad0e" +dependencies = [ + "alloy-json-rpc", + "alloy-transport", + "itertools 0.14.0", + "reqwest", + "serde_json", + "tower", + "tracing", + "url", +] + +[[package]] +name = "alloy-transport-ipc" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2ef85688e5ac2da72afc804e0a1f153a1f309f05a864b1998bbbed7804dbaab" +dependencies = [ + "alloy-json-rpc", + "alloy-pubsub", + "alloy-transport", + "bytes", + "futures", + "interprocess", + "pin-project", + "serde", + "serde_json", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "alloy-transport-ws" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f00445db69d63298e2b00a0ea1d859f00e6424a3144ffc5eba9c31da995e16" +dependencies = [ + "alloy-pubsub", + "alloy-transport", + "futures", + "http", + "serde_json", + "tokio", + "tokio-tungstenite 0.26.2", + "tracing", + "ws_stream_wasm", +] + +[[package]] +name = "alloy-trie" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f14b5d9b2c2173980202c6ff470d96e7c5e202c65a9f67884ad565226df7fbb" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "derive_more", + "nybbles", + "serde", + "smallvec", + "thiserror 2.0.18", + "tracing", +] + +[[package]] +name = "alloy-tx-macros" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fa0c53e8c1e1ef4d01066b01c737fb62fc9397ab52c6e7bb5669f97d281b9bc" +dependencies = [ + "darling 0.21.3", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "aquamarine" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f50776554130342de4836ba542aa85a4ddb361690d7e8df13774d7284c3d5c2" +dependencies = [ + "include_dir", + "itertools 0.10.5", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "ark-bls12-381" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3df4dcc01ff89867cd86b0da835f23c3f02738353aaee7dde7495af71363b8d5" +dependencies = [ + "ark-ec", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", +] + +[[package]] +name = "ark-bn254" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d69eab57e8d2663efa5c63135b2af4f396d66424f88954c21104125ab6b3e6bc" +dependencies = [ + "ark-ec", + "ark-ff 0.5.0", + "ark-r1cs-std", + "ark-std 0.5.0", +] + +[[package]] +name = "ark-ec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce" +dependencies = [ + "ahash", + "ark-ff 0.5.0", + "ark-poly", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "educe", + "fnv", + "hashbrown 0.15.5", + "itertools 0.13.0", + "num-bigint", + "num-integer", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.3.3", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.4.1", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70" +dependencies = [ + "ark-ff-asm 0.5.0", + "ark-ff-macros 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "arrayvec", + "digest 0.10.7", + "educe", + "itertools 0.13.0", + "num-bigint", + "num-traits", + "paste", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" +dependencies = [ + "quote", + "syn 2.0.117", +] + +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint", + "num-traits", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "ark-poly" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27" +dependencies = [ + "ahash", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "educe", + "fnv", + "hashbrown 0.15.5", +] + +[[package]] +name = "ark-r1cs-std" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "941551ef1df4c7a401de7068758db6503598e6f01850bdb2cfdb614a1f9dbea1" +dependencies = [ + "ark-ec", + "ark-ff 0.5.0", + "ark-relations", + "ark-std 0.5.0", + "educe", + "num-bigint", + "num-integer", + "num-traits", + "tracing", +] + +[[package]] +name = "ark-relations" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec46ddc93e7af44bcab5230937635b06fb5744464dd6a7e7b083e80ebd274384" +dependencies = [ + "ark-ff 0.5.0", + "ark-std 0.5.0", + "tracing", + "tracing-subscriber 0.2.25", +] + +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-std 0.4.0", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-serialize" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" +dependencies = [ + "ark-serialize-derive", + "ark-std 0.5.0", + "arrayvec", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "ark-std" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +dependencies = [ + "serde", +] + +[[package]] +name = "asn1_der" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4858a9d740c5007a9069007c3b4e91152d0506f13c1b31dd49051fd537656156" + +[[package]] +name = "async-compression" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0f9ee0f6e02ffd7ad5816e9464499fba7b3effd01123b515c41d1697c43dad1" +dependencies = [ + "compression-codecs", + "compression-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "async_io_stream" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" +dependencies = [ + "futures", + "pharos", + "rustc_version 0.4.1", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "aurora-engine-modexp" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "518bc5745a6264b5fd7b09dffb9667e400ee9e2bbe18555fac75e1fe9afa0df9" +dependencies = [ + "hex", + "num", +] + +[[package]] +name = "auto_impl" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "axum" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" +dependencies = [ + "axum-core", + "bytes", + "form_urlencoded", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "backon" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cffb0e931875b666fc4fcb20fee52e9bbd1ef836fd9e9e04ec21555f9f85f7ef" +dependencies = [ + "fastrand", + "tokio", +] + +[[package]] +name = "base-x" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base256emoji" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e9430d9a245a77c92176e649af6e275f20839a48389859d1661e9a128d077c" +dependencies = [ + "const-str", + "match-lookup", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + +[[package]] +name = "bimap" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags 2.11.0", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.117", +] + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitcoin-io" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dee39a0ee5b4095224a0cfc6bf4cc1baf0f9624b96b367e53b66d974e51d953" + +[[package]] +name = "bitcoin_hashes" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26ec84b80c482df901772e931a9a681e26a1b9ee2302edeff23cb30328745c8b" +dependencies = [ + "bitcoin-io", + "hex-conservative", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +dependencies = [ + "serde_core", +] + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "serde", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blst" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcdb4c7013139a150f9fc55d123186dbfaba0d912817466282c73ac49e71fb45" +dependencies = [ + "cc", + "glob", + "threadpool", + "zeroize", +] + +[[package]] +name = "boa_ast" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc119a5ad34c3f459062a96907f53358989b173d104258891bb74f95d93747e8" +dependencies = [ + "bitflags 2.11.0", + "boa_interner", + "boa_macros", + "boa_string", + "indexmap 2.13.0", + "num-bigint", + "rustc-hash", +] + +[[package]] +name = "boa_engine" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e637ec52ea66d76b0ca86180c259d6c7bb6e6a6e14b2f36b85099306d8b00cc3" +dependencies = [ + "aligned-vec", + "arrayvec", + "bitflags 2.11.0", + "boa_ast", + "boa_gc", + "boa_interner", + "boa_macros", + "boa_parser", + "boa_string", + "bytemuck", + "cfg-if", + "cow-utils", + "dashmap", + "dynify", + "fast-float2", + "float16", + "futures-channel", + "futures-concurrency", + "futures-lite", + "hashbrown 0.16.1", + "icu_normalizer", + "indexmap 2.13.0", + "intrusive-collections", + "itertools 0.14.0", + "num-bigint", + "num-integer", + "num-traits", + "num_enum", + "paste", + "portable-atomic", + "rand 0.9.2", + "regress", + "rustc-hash", + "ryu-js", + "serde", + "serde_json", + "small_btree", + "static_assertions", + "tag_ptr", + "tap", + "thin-vec", + "thiserror 2.0.18", + "time", + "xsum", +] + +[[package]] +name = "boa_gc" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1179f690cbfcbe5364cceee5f1cb577265bb6f07b0be6f210aabe270adcf9da" +dependencies = [ + "boa_macros", + "boa_string", + "hashbrown 0.16.1", + "thin-vec", +] + +[[package]] +name = "boa_interner" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9626505d33dc63d349662437297df1d3afd9d5fc4a2b3ad34e5e1ce879a78848" +dependencies = [ + "boa_gc", + "boa_macros", + "hashbrown 0.16.1", + "indexmap 2.13.0", + "once_cell", + "phf", + "rustc-hash", + "static_assertions", +] + +[[package]] +name = "boa_macros" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f36418a46544b152632c141b0a0b7a453cd69ca150caeef83aee9e2f4b48b7d" +dependencies = [ + "cfg-if", + "cow-utils", + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "boa_parser" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02f99bf5b684f0de946378fcfe5f38c3a0fbd51cbf83a0f39ff773a0e218541f" +dependencies = [ + "bitflags 2.11.0", + "boa_ast", + "boa_interner", + "boa_macros", + "fast-float2", + "icu_properties", + "num-bigint", + "num-traits", + "regress", + "rustc-hash", +] + +[[package]] +name = "boa_string" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45ce9d7aa5563a2e14eab111e2ae1a06a69a812f6c0c3d843196c9d03fbef440" +dependencies = [ + "fast-float2", + "itoa", + "paste", + "rustc-hash", + "ryu-js", + "static_assertions", +] + +[[package]] +name = "borsh" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" +dependencies = [ + "borsh-derive", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0686c856aa6aac0c4498f936d7d6a02df690f614c03e4d906d1018062b5c5e2c" +dependencies = [ + "once_cell", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "boyer-moore-magiclen" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7441b4796eb8a7107d4cd99d829810be75f5573e1081c37faa0e8094169ea0d6" +dependencies = [ + "debug-helper", +] + +[[package]] +name = "brotli" +version = "8.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "sha2", + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "byte-slice-cast" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +dependencies = [ + "serde", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.13+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" +dependencies = [ + "cc", + "pkg-config", +] + +[[package]] +name = "c-kzg" +version = "2.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6648ed1e4ea8e8a1a4a2c78e1cda29a3fd500bc622899c340d8525ea9a76b24a" +dependencies = [ + "blst", + "cc", + "glob", + "hex", + "libc", + "once_cell", + "serde", +] + +[[package]] +name = "camino" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" +dependencies = [ + "serde_core", +] + +[[package]] +name = "cargo-platform" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87a0c0e6148f11f01f32650a2ea02d532b2ad4e81d8bd41e6e565b5adc5e6082" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "cargo_metadata" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef987d17b0a113becdd19d3d0022d04d7ef41f9efe4f3fb63ac44ba61df3ade9" +dependencies = [ + "camino", + "cargo-platform", + "semver 1.0.27", + "serde", + "serde_json", + "thiserror 2.0.18", +] + +[[package]] +name = "castaway" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" +dependencies = [ + "rustversion", +] + +[[package]] +name = "cc" +version = "1.2.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "coins-bip32" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2073678591747aed4000dd468b97b14d7007f7936851d3f2f01846899f5ebf08" +dependencies = [ + "bs58", + "coins-core", + "digest 0.10.7", + "hmac", + "k256", + "serde", + "sha2", + "thiserror 1.0.69", +] + +[[package]] +name = "coins-bip39" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74b169b26623ff17e9db37a539fe4f15342080df39f129ef7631df7683d6d9d4" +dependencies = [ + "bitvec", + "coins-bip32", + "hmac", + "once_cell", + "pbkdf2", + "rand 0.8.5", + "sha2", + "thiserror 1.0.69", +] + +[[package]] +name = "coins-core" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b962ad8545e43a28e14e87377812ba9ae748dd4fd963f4c10e9fcc6d13475b" +dependencies = [ + "base64 0.21.7", + "bech32", + "bs58", + "const-hex", + "digest 0.10.7", + "generic-array", + "ripemd", + "serde", + "sha2", + "sha3", + "thiserror 1.0.69", +] + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "comfy-table" +version = "7.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958c5d6ecf1f214b4c2bbbbf6ab9523a864bd136dcf71a7e8904799acfe1ad47" +dependencies = [ + "crossterm", + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "compact_str" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb1325a1cece981e8a296ab8f0f9b63ae357bd0784a9faaf548cc7b480707a" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "rustversion", + "ryu", + "static_assertions", +] + +[[package]] +name = "compression-codecs" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb7b51a7d9c967fc26773061ba86150f19c50c0d65c887cb1fbe295fd16619b7" +dependencies = [ + "brotli", + "compression-core", + "flate2", + "memchr", + "zstd", + "zstd-safe", +] + +[[package]] +name = "compression-core" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" + +[[package]] +name = "concat-kdf" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d72c1252426a83be2092dd5884a5f6e3b8e7180f6891b6263d2c21b92ec8816" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "const-hex" +version = "1.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "531185e432bb31db1ecda541e9e7ab21468d4d844ad7505e0546a49b4945d49b" +dependencies = [ + "cfg-if", + "cpufeatures", + "proptest", + "serde_core", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const-str" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f421161cb492475f1661ddc9815a745a1c894592070661180fdec3d4872e9c3" + +[[package]] +name = "const_format" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + +[[package]] +name = "cow-utils" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "417bef24afe1460300965a25ff4a24b8b45ad011948302ec221e8a0a81eb2c79" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crossterm" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" +dependencies = [ + "bitflags 2.11.0", + "crossterm_winapi", + "derive_more", + "document-features", + "mio", + "parking_lot", + "rustix", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "rustc_version 0.4.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +dependencies = [ + "darling_core 0.21.3", + "darling_macro 0.21.3", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core 0.23.0", + "darling_macro 0.23.0", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.117", +] + +[[package]] +name = "darling_core" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "serde", + "strsim", + "syn 2.0.117", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.117", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core 0.20.11", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "darling_macro" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +dependencies = [ + "darling_core 0.21.3", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core 0.23.0", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", + "serde", +] + +[[package]] +name = "data-encoding" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" + +[[package]] +name = "data-encoding-macro" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8142a83c17aa9461d637e649271eae18bf2edd00e91f2e105df36c3c16355bdb" +dependencies = [ + "data-encoding", + "data-encoding-macro-internal", +] + +[[package]] +name = "data-encoding-macro-internal" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab67060fc6b8ef687992d439ca0fa36e7ed17e9a0b16b25b601e8757df720de" +dependencies = [ + "data-encoding", + "syn 2.0.117", +] + +[[package]] +name = "debug-helper" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e" + +[[package]] +name = "delay_map" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88e365f083a5cb5972d50ce8b1b2c9f125dc5ec0f50c0248cfb568ae59efcf0b" +dependencies = [ + "futures", + "tokio", + "tokio-util", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "powerfmt", + "serde_core", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive-where" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d08b3a0bcc0d079199cd476b2cae8435016ec11d1c0986c6901c5ac223041534" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling 0.20.11", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn 2.0.117", +] + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version 0.4.1", + "syn 2.0.117", + "unicode-xid", +] + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users 0.5.2", + "windows-sys 0.61.2", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users 0.4.6", + "winapi", +] + +[[package]] +name = "discv5" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f170f4f6ed0e1df52bf43b403899f0081917ecf1500bfe312505cc3b515a8899" +dependencies = [ + "aes", + "aes-gcm", + "alloy-rlp", + "arrayvec", + "ctr", + "delay_map", + "enr", + "fnv", + "futures", + "hashlink 0.9.1", + "hex", + "hkdf", + "lazy_static", + "libp2p-identity", + "lru 0.12.5", + "more-asserts", + "multiaddr", + "parking_lot", + "rand 0.8.5", + "smallvec", + "socket2 0.5.10", + "tokio", + "tracing", + "uint 0.10.0", + "zeroize", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "doctest-file" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2db04e74f0a9a93103b50e90b96024c9b2bdca8bce6a632ec71b88736d3d359" + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "dynify" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81acb15628a3e22358bf73de5e7e62360b8a777dbcb5fc9ac7dfa9ae73723747" +dependencies = [ + "dynify-macros", +] + +[[package]] +name = "dynify-macros" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec431cd708430d5029356535259c5d645d60edd3d39c54e5eea9782d46caa7d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "serdect", + "signature", + "spki", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core 0.6.4", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "educe" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +dependencies = [ + "serde", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "serdect", + "subtle", + "zeroize", +] + +[[package]] +name = "enr" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "851bd664a3d3a3c175cff92b2f0df02df3c541b4895d0ae307611827aae46152" +dependencies = [ + "alloy-rlp", + "base64 0.22.1", + "bytes", + "ed25519-dalek", + "hex", + "k256", + "log", + "rand 0.8.5", + "secp256k1 0.30.0", + "serde", + "sha3", + "zeroize", +] + +[[package]] +name = "enum-as-inner" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "enum-ordinalize" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a1091a7bb1f8f2c4b28f1fe2cef4980ca2d410a3d727d67ecc3178c9b0800f0" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "equator" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" +dependencies = [ + "equator-macro", +] + +[[package]] +name = "equator-macro" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "ethereum_hashing" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c853bd72c9e5787f8aafc3df2907c2ed03cff3150c3acd94e2e53a98ab70a8ab" +dependencies = [ + "cpufeatures", + "ring", + "sha2", +] + +[[package]] +name = "ethereum_serde_utils" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dc1355dbb41fbbd34ec28d4fb2a57d9a70c67ac3c19f6a5ca4d4a176b9e997a" +dependencies = [ + "alloy-primitives", + "hex", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "ethereum_ssz" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dcddb2554d19cde19b099fadddde576929d7a4d0c1cd3512d1fd95cf174375c" +dependencies = [ + "alloy-primitives", + "ethereum_serde_utils", + "itertools 0.13.0", + "serde", + "serde_derive", + "smallvec", + "typenum", +] + +[[package]] +name = "ethereum_ssz" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2128a84f7a3850d54ee343334e3392cca61f9f6aa9441eec481b9394b43c238b" +dependencies = [ + "alloy-primitives", + "ethereum_serde_utils", + "itertools 0.14.0", + "serde", + "serde_derive", + "smallvec", + "typenum", +] + +[[package]] +name = "ethereum_ssz_derive" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a657b6b3b7e153637dc6bdc6566ad9279d9ee11a15b12cfb24a2e04360637e9f" +dependencies = [ + "darling 0.20.11", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "ethereum_ssz_derive" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd596f91cff004fc8d02be44c21c0f9b93140a04b66027ae052f5f8e05b48eba" +dependencies = [ + "darling 0.23.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "fast-float2" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8eb564c5c7423d25c886fb561d1e4ee69f72354d16918afa32c08811f6b6a55" + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fastrlp" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + +[[package]] +name = "fastrlp" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce8dba4714ef14b8274c371879b175aa55b16b30f269663f19d576f380018dc4" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + +[[package]] +name = "fdlimit" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e182f7dbc2ef73d9ef67351c5fbbea084729c48362d3ce9dd44c28e32e277fe5" +dependencies = [ + "libc", + "thiserror 1.0.69", +] + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "filetime" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" +dependencies = [ + "cfg-if", + "libc", + "libredox", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fixed-cache" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41c7aa69c00ebccf06c3fa7ffe2a6cf26a58b5fe4deabfe646285ff48136a8f" +dependencies = [ + "equivalent", + "rapidhash", + "typeid", +] + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fixed-map" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86ed19add84e8cb9e8cc5f7074de0324247149ffef0b851e215fb0edc50c229b" +dependencies = [ + "fixed-map-derive", + "serde", +] + +[[package]] +name = "fixed-map-derive" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dc7a9cb3326bafb80642c5ce99b39a2c0702d4bfa8ee8a3e773791a6cbe2407" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "float16" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bffafbd079d520191c7c2779ae9cf757601266cf4167d3f659ff09617ff8483" +dependencies = [ + "cfg-if", + "rustc_version 0.2.3", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-concurrency" +version = "7.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175cd8cca9e1d45b87f18ffa75088f2099e3c4fe5e2f83e42de112560bea8ea6" +dependencies = [ + "fixedbitset", + "futures-core", + "futures-lite", + "pin-project", + "smallvec", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" +dependencies = [ + "gloo-timers", + "send_wrapper 0.4.0", +] + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "futures-utils-wasm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi 5.3.0", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "wasip2", + "wasip3", +] + +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "git2" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b88256088d75a56f8ecfa070513a775dd9107f6530ef14919dac831af9cfe2b" +dependencies = [ + "bitflags 2.11.0", + "libc", + "libgit2-sys", + "log", + "url", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "gloo-net" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06f627b1a58ca3d42b45d6104bf1e1a03799df472df00988b6ba21accc10580" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils", + "http", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror 1.0.69", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "h2" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap 2.13.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", + "serde", + "serde_core", +] + +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown 0.14.5", +] + +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.5", +] + +[[package]] +name = "hdrhistogram" +version = "7.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" +dependencies = [ + "byteorder", + "num-traits", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-conservative" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda06d18ac606267c40c04e41b9947729bf8b9efe74bd4e82b61a5f26a510b9f" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "hickory-proto" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8a6fe56c0038198998a6f217ca4e7ef3a5e51f46163bd6dd60b5c71ca6c6502" +dependencies = [ + "async-trait", + "cfg-if", + "data-encoding", + "enum-as-inner", + "futures-channel", + "futures-io", + "futures-util", + "idna", + "ipnet", + "once_cell", + "rand 0.9.2", + "ring", + "serde", + "thiserror 2.0.18", + "tinyvec", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "hickory-resolver" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc62a9a99b0bfb44d2ab95a7208ac952d31060efc16241c87eaf36406fecf87a" +dependencies = [ + "cfg-if", + "futures-util", + "hickory-proto", + "ipconfig", + "moka", + "once_cell", + "parking_lot", + "rand 0.9.2", + "resolv-conf", + "serde", + "smallvec", + "thiserror 2.0.18", + "tokio", + "tracing", +] + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "http-range-header" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "human_bytes" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91f255a4535024abf7640cb288260811fc14794f62b063652ed349f9a6c2348e" + +[[package]] +name = "humantime" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" + +[[package]] +name = "humantime-serde" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" +dependencies = [ + "humantime", + "serde", +] + +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "log", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots 1.0.6", +] + +[[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2 0.6.3", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "serde", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b24a59706036ba941c9476a55cd57b82b77f38a3c667d637ee7cabbc85eaedc" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5a97b8ac6235e69506e8dacfb2adf38461d2ce6d3e9bd9c94c4cbc3cd4400a4" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "serde", + "stable_deref_trait", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "if-addrs" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf39cc0423ee66021dc5eccface85580e4a001e0c5288bae8bea7ecb69225e90" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "include_dir" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd" +dependencies = [ + "include_dir_macros", +] + +[[package]] +name = "include_dir_macros" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "indenter" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + +[[package]] +name = "indoc" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] + +[[package]] +name = "inotify" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd5b3eaf1a28b758ac0faa5a4254e8ab2705605496f1b1f3fbbc3988ad73d199" +dependencies = [ + "bitflags 2.11.0", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "instability" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eb2d60ef19920a3a9193c3e371f726ec1dafc045dac788d0fb3704272458971" +dependencies = [ + "darling 0.23.0", + "indoc", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "interprocess" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6be5e5c847dbdb44564bd85294740d031f4f8aeb3464e5375ef7141f7538db69" +dependencies = [ + "doctest-file", + "futures-core", + "libc", + "recvmsg", + "tokio", + "widestring", + "windows-sys 0.52.0", +] + +[[package]] +name = "intrusive-collections" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "189d0897e4cbe8c75efedf3502c18c887b05046e59d28404d4d8e46cbc4d1e86" +dependencies = [ + "memoffset", +] + +[[package]] +name = "ipconfig" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" +dependencies = [ + "socket2 0.5.10", + "widestring", + "windows-sys 0.48.0", + "winreg", +] + +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + +[[package]] +name = "iri-string" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "jsonrpsee" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f3f48dc3e6b8bd21e15436c1ddd0bc22a6a54e8ec46fedd6adf3425f396ec6a" +dependencies = [ + "jsonrpsee-client-transport", + "jsonrpsee-core", + "jsonrpsee-http-client", + "jsonrpsee-proc-macros", + "jsonrpsee-server", + "jsonrpsee-types", + "jsonrpsee-wasm-client", + "jsonrpsee-ws-client", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-client-transport" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf36eb27f8e13fa93dcb50ccb44c417e25b818cfa1a481b5470cd07b19c60b98" +dependencies = [ + "base64 0.22.1", + "futures-channel", + "futures-util", + "gloo-net", + "http", + "jsonrpsee-core", + "pin-project", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "soketto", + "thiserror 2.0.18", + "tokio", + "tokio-rustls", + "tokio-util", + "tracing", + "url", +] + +[[package]] +name = "jsonrpsee-core" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "316c96719901f05d1137f19ba598b5fe9c9bc39f4335f67f6be8613921946480" +dependencies = [ + "async-trait", + "bytes", + "futures-timer", + "futures-util", + "http", + "http-body", + "http-body-util", + "jsonrpsee-types", + "parking_lot", + "pin-project", + "rand 0.9.2", + "rustc-hash", + "serde", + "serde_json", + "thiserror 2.0.18", + "tokio", + "tokio-stream", + "tower", + "tracing", + "wasm-bindgen-futures", +] + +[[package]] +name = "jsonrpsee-http-client" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790bedefcec85321e007ff3af84b4e417540d5c87b3c9779b9e247d1bcc3dab8" +dependencies = [ + "base64 0.22.1", + "http-body", + "hyper", + "hyper-rustls", + "hyper-util", + "jsonrpsee-core", + "jsonrpsee-types", + "rustls", + "rustls-platform-verifier", + "serde", + "serde_json", + "thiserror 2.0.18", + "tokio", + "tower", + "url", +] + +[[package]] +name = "jsonrpsee-proc-macros" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da3f8ab5ce1bb124b6d082e62dffe997578ceaf0aeb9f3174a214589dc00f07" +dependencies = [ + "heck", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "jsonrpsee-server" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c51b7c290bb68ce3af2d029648148403863b982f138484a73f02a9dd52dbd7f" +dependencies = [ + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "jsonrpsee-core", + "jsonrpsee-types", + "pin-project", + "route-recognizer", + "serde", + "serde_json", + "soketto", + "thiserror 2.0.18", + "tokio", + "tokio-stream", + "tokio-util", + "tower", + "tracing", +] + +[[package]] +name = "jsonrpsee-types" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc88ff4688e43cc3fa9883a8a95c6fa27aa2e76c96e610b737b6554d650d7fd5" +dependencies = [ + "http", + "serde", + "serde_json", + "thiserror 2.0.18", +] + +[[package]] +name = "jsonrpsee-wasm-client" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7902885de4779f711a95d82c8da2d7e5f9f3a7c7cfa44d51c067fd1c29d72a3c" +dependencies = [ + "jsonrpsee-client-transport", + "jsonrpsee-core", + "jsonrpsee-types", + "tower", +] + +[[package]] +name = "jsonrpsee-ws-client" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6fceceeb05301cc4c065ab3bd2fa990d41ff4eb44e4ca1b30fa99c057c3e79" +dependencies = [ + "http", + "jsonrpsee-client-transport", + "jsonrpsee-core", + "jsonrpsee-types", + "tower", + "url", +] + +[[package]] +name = "jsonwebtoken" +version = "9.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" +dependencies = [ + "base64 0.22.1", + "js-sys", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "serdect", + "sha2", + "signature", +] + +[[package]] +name = "kasuari" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bde5057d6143cc94e861d90f591b9303d6716c6b9602309150bd068853c10899" +dependencies = [ + "hashbrown 0.16.1", + "portable-atomic", + "thiserror 2.0.18", +] + +[[package]] +name = "keccak" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "keccak-asm" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b646a74e746cd25045aa0fd42f4f7f78aa6d119380182c7e63a5593c4ab8df6f" +dependencies = [ + "digest 0.10.7", + "sha3-asm", +] + +[[package]] +name = "kqueue" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.183" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" + +[[package]] +name = "libgit2-sys" +version = "0.18.3+1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9b3acc4b91781bb0b3386669d325163746af5f6e4f73e6d2d630e09a35f3487" +dependencies = [ + "cc", + "libc", + "libz-sys", + "pkg-config", +] + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "libp2p-identity" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c7892c221730ba55f7196e98b0b8ba5e04b4155651736036628e9f73ed6fc3" +dependencies = [ + "asn1_der", + "bs58", + "ed25519-dalek", + "hkdf", + "k256", + "multihash", + "quick-protobuf", + "sha2", + "thiserror 2.0.18", + "tracing", + "zeroize", +] + +[[package]] +name = "libproc" +version = "0.14.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a54ad7278b8bc5301d5ffd2a94251c004feb971feba96c971ea4063645990757" +dependencies = [ + "bindgen", + "errno", + "libc", +] + +[[package]] +name = "libredox" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a" +dependencies = [ + "bitflags 2.11.0", + "libc", + "plain", + "redox_syscall 0.7.3", +] + +[[package]] +name = "librocksdb-sys" +version = "0.17.3+10.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cef2a00ee60fe526157c9023edab23943fae1ce2ab6f4abb2a807c1746835de9" +dependencies = [ + "bindgen", + "bzip2-sys", + "cc", + "libc", + "libz-sys", + "lz4-sys", + "tikv-jemalloc-sys", + "zstd-sys", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "libz-sys" +version = "1.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52f4c29e2a68ac30c9087e1b772dc9f44a2b66ed44edf2266cf2be9b03dafc1" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "line-clipping" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4de44e98ddbf09375cbf4d17714d18f39195f4f4894e8524501726fd9a8a4a" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linked_hash_set" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "984fb35d06508d1e69fc91050cceba9c0b748f983e6739fa2c7a9237154c52c8" +dependencies = [ + "linked-hash-map", + "serde_core", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", + "serde", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown 0.15.5", +] + +[[package]] +name = "lru" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593" +dependencies = [ + "hashbrown 0.16.1", +] + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "lz4" +version = "1.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a20b523e860d03443e98350ceaac5e71c6ba89aea7d960769ec3ce37f4de5af4" +dependencies = [ + "lz4-sys", +] + +[[package]] +name = "lz4-sys" +version = "1.11.1+lz4-1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bd8c0d6c6ed0cd30b3652886bb8711dc4bb01d637a68105a3d5158039b418e6" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "lz4_flex" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98c23545df7ecf1b16c303910a69b079e8e251d60f7dd2cc9b4177f2afaf1746" + +[[package]] +name = "mach2" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a1b95cd5421ec55b445b5ae102f5ea0e768de1f82bd3001e11f426c269c3aea" +dependencies = [ + "libc", +] + +[[package]] +name = "mach2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dae608c151f68243f2b000364e1f7b186d9c29845f7d2d85bd31b9ad77ad552b" + +[[package]] +name = "macro-string" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "match-lookup" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "757aee279b8bdbb9f9e676796fd459e4207a1f986e87886700abf589f5abf771" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "memmap2" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "metrics" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5312e9ba3771cfa961b585728215e3d972c950a3eed9252aa093d6301277e8" +dependencies = [ + "ahash", + "portable-atomic", +] + +[[package]] +name = "metrics-derive" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "161ab904c2c62e7bda0f7562bf22f96440ca35ff79e66c800cbac298f2f4f5ec" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "metrics-exporter-prometheus" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3589659543c04c7dc5526ec858591015b87cd8746583b51b48ef4353f99dbcda" +dependencies = [ + "base64 0.22.1", + "indexmap 2.13.0", + "metrics", + "metrics-util", + "quanta", + "thiserror 2.0.18", +] + +[[package]] +name = "metrics-process" +version = "2.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4268d87f64a752f5a651314fc683f04da10be65701ea3e721ba4d74f79163cac" +dependencies = [ + "libc", + "libproc", + "mach2 0.6.0", + "metrics", + "once_cell", + "procfs", + "rlimit", + "windows", +] + +[[package]] +name = "metrics-util" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdfb1365fea27e6dd9dc1dbc19f570198bc86914533ad639dae939635f096be4" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", + "hashbrown 0.16.1", + "metrics", + "quanta", + "rand 0.9.2", + "rand_xoshiro", + "sketches-ddsketch", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "modular-bitfield" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2956e537fc68236d2aa048f55704f231cc93f1c4de42fe1ecb5bd7938061fc4a" +dependencies = [ + "modular-bitfield-impl", + "static_assertions", +] + +[[package]] +name = "modular-bitfield-impl" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b43b4fd69e3437618106f7754f34021b831a514f9e1a98ae863cabcd8d8dad" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "moka" +version = "0.12.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85f8024e1c8e71c778968af91d43700ce1d11b219d127d79fb2934153b82b42b" +dependencies = [ + "crossbeam-channel", + "crossbeam-epoch", + "crossbeam-utils", + "equivalent", + "parking_lot", + "portable-atomic", + "smallvec", + "tagptr", + "uuid", +] + +[[package]] +name = "more-asserts" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fafa6961cabd9c63bcd77a45d7e3b7f3b552b70417831fb0f56db717e72407e" + +[[package]] +name = "multiaddr" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe6351f60b488e04c1d21bc69e56b89cb3f5e8f5d22557d6e8031bdfd79b6961" +dependencies = [ + "arrayref", + "byteorder", + "data-encoding", + "libp2p-identity", + "multibase", + "multihash", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint", + "url", +] + +[[package]] +name = "multibase" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8694bb4835f452b0e3bb06dbebb1d6fc5385b6ca1caf2e55fd165c042390ec77" +dependencies = [ + "base-x", + "base256emoji", + "data-encoding", + "data-encoding-macro", +] + +[[package]] +name = "multihash" +version = "0.19.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b430e7953c29dd6a09afc29ff0bb69c6e306329ee6794700aee27b76a1aea8d" +dependencies = [ + "core2", + "unsigned-varint", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "notify" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" +dependencies = [ + "bitflags 2.11.0", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "log", + "mio", + "notify-types", + "walkdir", + "windows-sys 0.60.2", +] + +[[package]] +name = "notify-types" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "ntapi" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3b335231dfd352ffb0f8017f3b6027a4917f7df785ea2143d8af2adc66980ae" +dependencies = [ + "winapi", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + +[[package]] +name = "nybbles" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d49ff0c0d00d4a502b39df9af3a525e1efeb14b9dabb5bb83335284c1309210" +dependencies = [ + "alloy-rlp", + "cfg-if", + "proptest", + "ruint", + "serde", + "smallvec", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "objc2-io-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15" +dependencies = [ + "libc", + "objc2-core-foundation", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" +dependencies = [ + "critical-section", + "portable-atomic", +] + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "op-alloy" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9b8fee21003dd4f076563de9b9d26f8c97840157ef78593cd7f262c5ca99848" +dependencies = [ + "op-alloy-consensus", + "op-alloy-network", + "op-alloy-provider", + "op-alloy-rpc-types", + "op-alloy-rpc-types-engine", +] + +[[package]] +name = "op-alloy-consensus" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736381a95471d23e267263cfcee9e1d96d30b9754a94a2819148f83379de8a86" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-network", + "alloy-primitives", + "alloy-rlp", + "alloy-rpc-types-eth", + "alloy-serde", + "derive_more", + "serde", + "serde_with", + "thiserror 2.0.18", +] + +[[package]] +name = "op-alloy-network" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4034183dca6bff6632e7c24c92e75ff5f0eabb58144edb4d8241814851334d47" +dependencies = [ + "alloy-consensus", + "alloy-network", + "alloy-primitives", + "alloy-provider", + "alloy-rpc-types-eth", + "alloy-signer", + "op-alloy-consensus", + "op-alloy-rpc-types", +] + +[[package]] +name = "op-alloy-provider" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6753d90efbaa8ea8bcb89c1737408ca85fa60d7adb875049d3f382c063666f86" +dependencies = [ + "alloy-network", + "alloy-primitives", + "alloy-provider", + "alloy-rpc-types-engine", + "alloy-transport", + "async-trait", + "op-alloy-rpc-types-engine", +] + +[[package]] +name = "op-alloy-rpc-types" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd87c6b9e5b6eee8d6b76f41b04368dca0e9f38d83338e5b00e730c282098a4" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-network-primitives", + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "derive_more", + "op-alloy-consensus", + "serde", + "serde_json", + "thiserror 2.0.18", +] + +[[package]] +name = "op-alloy-rpc-types-engine" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77727699310a18cdeed32da3928c709e2704043b6584ed416397d5da65694efc" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-rpc-types-engine", + "alloy-serde", + "derive_more", + "ethereum_ssz 0.9.1", + "ethereum_ssz_derive 0.9.1", + "op-alloy-consensus", + "serde", + "sha2", + "snap", + "thiserror 2.0.18", +] + +[[package]] +name = "op-revm" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79c92b75162c2ed1661849fa51683b11254a5b661798360a2c24be918edafd40" +dependencies = [ + "auto_impl", + "revm", + "serde", +] + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "opentelemetry" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b84bcd6ae87133e903af7ef497404dda70c60d0ea14895fc8a5e6722754fc2a0" +dependencies = [ + "futures-core", + "futures-sink", + "js-sys", + "pin-project-lite", + "thiserror 2.0.18", + "tracing", +] + +[[package]] +name = "opentelemetry-appender-tracing" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef6a1ac5ca3accf562b8c306fa8483c85f4390f768185ab775f242f7fe8fdcc2" +dependencies = [ + "opentelemetry", + "tracing", + "tracing-core", + "tracing-subscriber 0.3.23", +] + +[[package]] +name = "opentelemetry-http" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7a6d09a73194e6b66df7c8f1b680f156d916a1a942abf2de06823dd02b7855d" +dependencies = [ + "async-trait", + "bytes", + "http", + "opentelemetry", + "reqwest", +] + +[[package]] +name = "opentelemetry-otlp" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2366db2dca4d2ad033cad11e6ee42844fd727007af5ad04a1730f4cb8163bf" +dependencies = [ + "http", + "opentelemetry", + "opentelemetry-http", + "opentelemetry-proto", + "opentelemetry_sdk", + "prost", + "reqwest", + "thiserror 2.0.18", + "tokio", + "tonic", + "tracing", +] + +[[package]] +name = "opentelemetry-proto" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7175df06de5eaee9909d4805a3d07e28bb752c34cab57fa9cff549da596b30f" +dependencies = [ + "opentelemetry", + "opentelemetry_sdk", + "prost", + "tonic", + "tonic-prost", +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e62e29dfe041afb8ed2a6c9737ab57db4907285d999ef8ad3a59092a36bdc846" + +[[package]] +name = "opentelemetry_sdk" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ae4f5991976fd48df6d843de219ca6d31b01daaab2dad5af2badeded372bd" +dependencies = [ + "futures-channel", + "futures-executor", + "futures-util", + "opentelemetry", + "percent-encoding", + "rand 0.9.2", + "thiserror 2.0.18", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "page_size" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "parity-scale-codec" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "bytes", + "const_format", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "rustversion", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.18", + "smallvec", + "windows-link", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest 0.10.7", + "hmac", +] + +[[package]] +name = "pem" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" +dependencies = [ + "base64 0.22.1", + "serde_core", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pest" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" +dependencies = [ + "memchr", + "ucd-trie", +] + +[[package]] +name = "pharos" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" +dependencies = [ + "futures", + "rustc_version 0.4.1", +] + +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros", + "phf_shared", + "serde", +] + +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared", +] + +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project" +version = "1.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "pretty_assertions" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" +dependencies = [ + "diff", + "yansi", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.117", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash", + "impl-codec", + "uint 0.9.5", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "procfs" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25485360a54d6861439d60facef26de713b1e126bf015ec8f98239467a2b82f7" +dependencies = [ + "bitflags 2.11.0", + "chrono", + "flate2", + "procfs-core", + "rustix", +] + +[[package]] +name = "procfs-core" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6401bf7b6af22f78b563665d15a22e9aef27775b79b149a66ca022468a4e405" +dependencies = [ + "bitflags 2.11.0", + "chrono", + "hex", +] + +[[package]] +name = "proptest" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37566cb3fdacef14c0737f9546df7cfeadbfbc9fef10991038bf5015d0c80532" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags 2.11.0", + "num-traits", + "rand 0.9.2", + "rand_chacha 0.9.0", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "prost" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" +dependencies = [ + "anyhow", + "itertools 0.14.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "quanta" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3ab5a9d756f0d97bdc89019bd2e4ea098cf9cde50ee7564dde6b81ccc8f06c7" +dependencies = [ + "crossbeam-utils", + "libc", + "once_cell", + "raw-cpuid", + "wasi", + "web-sys", + "winapi", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quick-protobuf" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6da84cc204722a989e01ba2f6e1e276e190f22263d0cb6ce8526fcdb0d2e1f" +dependencies = [ + "byteorder", +] + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2 0.6.3", + "thiserror 2.0.18", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.2", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2 0.6.3", + "tracing", + "windows-sys 0.60.2", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", + "serde", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", + "serde", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", + "serde", +] + +[[package]] +name = "rand_xorshift" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +dependencies = [ + "rand_core 0.9.5", +] + +[[package]] +name = "rand_xoshiro" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f703f4665700daf5512dcca5f43afa6af89f09db47fb56be587f80636bda2d41" +dependencies = [ + "rand_core 0.9.5", +] + +[[package]] +name = "rapidhash" +version = "4.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e48930979c155e2f33aa36ab3119b5ee81332beb6482199a8ecd6029b80b59" +dependencies = [ + "rand 0.9.2", + "rustversion", +] + +[[package]] +name = "ratatui" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1ce67fb8ba4446454d1c8dbaeda0557ff5e94d39d5e5ed7f10a65eb4c8266bc" +dependencies = [ + "instability", + "ratatui-core", + "ratatui-crossterm", + "ratatui-widgets", +] + +[[package]] +name = "ratatui-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef8dea09a92caaf73bff7adb70b76162e5937524058a7e5bff37869cbbec293" +dependencies = [ + "bitflags 2.11.0", + "compact_str", + "hashbrown 0.16.1", + "indoc", + "itertools 0.14.0", + "kasuari", + "lru 0.16.3", + "strum", + "thiserror 2.0.18", + "unicode-segmentation", + "unicode-truncate", + "unicode-width", +] + +[[package]] +name = "ratatui-crossterm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "577c9b9f652b4c121fb25c6a391dd06406d3b092ba68827e6d2f09550edc54b3" +dependencies = [ + "cfg-if", + "crossterm", + "instability", + "ratatui-core", +] + +[[package]] +name = "ratatui-widgets" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7dbfa023cd4e604c2553483820c5fe8aa9d71a42eea5aa77c6e7f35756612db" +dependencies = [ + "bitflags 2.11.0", + "hashbrown 0.16.1", + "indoc", + "instability", + "itertools 0.14.0", + "line-clipping", + "ratatui-core", + "strum", + "time", + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "raw-cpuid" +version = "11.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "recvmsg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "redox_syscall" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 1.0.69", +] + +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 2.0.18", +] + +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "regress" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2057b2325e68a893284d1538021ab90279adac1139957ca2a74426c6f118fb48" +dependencies = [ + "hashbrown 0.16.1", + "memchr", +] + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "webpki-roots 1.0.6", +] + +[[package]] +name = "resolv-conf" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7" + +[[package]] +name = "reth" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types", + "aquamarine", + "clap", + "reth-chainspec", + "reth-cli-runner", + "reth-cli-util", + "reth-consensus", + "reth-consensus-common", + "reth-db", + "reth-ethereum-cli", + "reth-ethereum-payload-builder", + "reth-network", + "reth-network-api", + "reth-node-api", + "reth-node-builder", + "reth-node-core", + "reth-node-ethereum", + "reth-node-metrics", + "reth-payload-builder", + "reth-payload-primitives", + "reth-primitives", + "reth-provider", + "reth-revm", + "reth-rpc", + "reth-rpc-api", + "reth-rpc-builder", + "reth-rpc-convert", + "reth-rpc-eth-types", + "reth-rpc-server-types", + "reth-tasks", + "reth-transaction-pool", + "tracing", +] + +[[package]] +name = "reth-basic-payload-builder" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "futures-core", + "futures-util", + "metrics", + "reth-chain-state", + "reth-metrics", + "reth-payload-builder", + "reth-payload-builder-primitives", + "reth-payload-primitives", + "reth-primitives-traits", + "reth-revm", + "reth-storage-api", + "reth-tasks", + "tokio", + "tracing", +] + +[[package]] +name = "reth-chain-state" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "derive_more", + "metrics", + "parking_lot", + "pin-project", + "rand 0.9.2", + "rayon", + "reth-chainspec", + "reth-errors", + "reth-ethereum-primitives", + "reth-execution-types", + "reth-metrics", + "reth-primitives-traits", + "reth-storage-api", + "reth-trie", + "revm-database", + "revm-state", + "serde", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "reth-chainspec" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-chains", + "alloy-consensus", + "alloy-eips", + "alloy-evm", + "alloy-genesis", + "alloy-primitives", + "alloy-trie", + "auto_impl", + "derive_more", + "reth-ethereum-forks", + "reth-network-peers", + "reth-primitives-traits", + "serde_json", +] + +[[package]] +name = "reth-cli" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-genesis", + "clap", + "eyre", + "reth-cli-runner", + "reth-db", + "serde_json", + "shellexpand", +] + +[[package]] +name = "reth-cli-commands" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-chains", + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "backon", + "clap", + "comfy-table", + "crossterm", + "eyre", + "fdlimit", + "futures", + "human_bytes", + "humantime", + "itertools 0.14.0", + "lz4", + "metrics", + "parking_lot", + "ratatui", + "reqwest", + "reth-chainspec", + "reth-cli", + "reth-cli-runner", + "reth-cli-util", + "reth-codecs", + "reth-config", + "reth-consensus", + "reth-db", + "reth-db-api", + "reth-db-common", + "reth-discv4", + "reth-discv5", + "reth-downloaders", + "reth-ecies", + "reth-era", + "reth-era-downloader", + "reth-era-utils", + "reth-eth-wire", + "reth-etl", + "reth-evm", + "reth-exex", + "reth-fs-util", + "reth-net-nat", + "reth-network", + "reth-network-p2p", + "reth-network-peers", + "reth-node-api", + "reth-node-builder", + "reth-node-core", + "reth-node-events", + "reth-node-metrics", + "reth-primitives-traits", + "reth-provider", + "reth-prune", + "reth-revm", + "reth-stages", + "reth-static-file", + "reth-static-file-types", + "reth-storage-api", + "reth-tasks", + "reth-trie", + "reth-trie-common", + "reth-trie-db", + "secp256k1 0.30.0", + "serde", + "serde_json", + "tar", + "tokio", + "tokio-stream", + "toml", + "tracing", + "url", + "zstd", +] + +[[package]] +name = "reth-cli-runner" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "reth-tasks", + "tokio", + "tracing", +] + +[[package]] +name = "reth-cli-util" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "cfg-if", + "eyre", + "libc", + "rand 0.8.5", + "reth-fs-util", + "secp256k1 0.30.0", + "serde", + "thiserror 2.0.18", + "tikv-jemallocator", +] + +[[package]] +name = "reth-codecs" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-genesis", + "alloy-primitives", + "alloy-trie", + "bytes", + "modular-bitfield", + "op-alloy-consensus", + "reth-codecs-derive", + "reth-zstd-compressors", + "serde", +] + +[[package]] +name = "reth-codecs-derive" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "reth-config" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "eyre", + "humantime-serde", + "reth-network-types", + "reth-prune-types", + "reth-stages-types", + "reth-static-file-types", + "serde", + "toml", + "url", +] + +[[package]] +name = "reth-consensus" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-primitives", + "auto_impl", + "reth-execution-types", + "reth-primitives-traits", + "thiserror 2.0.18", +] + +[[package]] +name = "reth-consensus-common" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "reth-chainspec", + "reth-consensus", + "reth-primitives-traits", +] + +[[package]] +name = "reth-consensus-debug-client" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-json-rpc", + "alloy-primitives", + "alloy-provider", + "alloy-rpc-types-engine", + "alloy-transport", + "auto_impl", + "derive_more", + "eyre", + "futures", + "reqwest", + "reth-node-api", + "reth-primitives-traits", + "reth-tracing", + "ringbuffer", + "serde", + "serde_json", + "tokio", +] + +[[package]] +name = "reth-db" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-primitives", + "derive_more", + "eyre", + "metrics", + "page_size", + "reth-db-api", + "reth-fs-util", + "reth-libmdbx", + "reth-metrics", + "reth-nippy-jar", + "reth-static-file-types", + "reth-storage-errors", + "reth-tracing", + "rustc-hash", + "strum", + "sysinfo", + "thiserror 2.0.18", + "tracing", +] + +[[package]] +name = "reth-db-api" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-genesis", + "alloy-primitives", + "arrayvec", + "bytes", + "derive_more", + "metrics", + "modular-bitfield", + "parity-scale-codec", + "reth-codecs", + "reth-db-models", + "reth-ethereum-primitives", + "reth-primitives-traits", + "reth-prune-types", + "reth-stages-types", + "reth-storage-errors", + "reth-trie-common", + "roaring", + "serde", +] + +[[package]] +name = "reth-db-common" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-genesis", + "alloy-primitives", + "boyer-moore-magiclen", + "eyre", + "reth-chainspec", + "reth-codecs", + "reth-config", + "reth-db-api", + "reth-etl", + "reth-execution-errors", + "reth-fs-util", + "reth-node-types", + "reth-primitives-traits", + "reth-provider", + "reth-stages-types", + "reth-static-file-types", + "reth-trie", + "reth-trie-db", + "serde", + "serde_json", + "thiserror 2.0.18", + "tracing", +] + +[[package]] +name = "reth-db-models" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "bytes", + "modular-bitfield", + "reth-codecs", + "reth-primitives-traits", + "serde", +] + +[[package]] +name = "reth-discv4" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "discv5", + "enr", + "itertools 0.14.0", + "parking_lot", + "rand 0.8.5", + "reth-ethereum-forks", + "reth-net-banlist", + "reth-net-nat", + "reth-network-peers", + "schnellru", + "secp256k1 0.30.0", + "serde", + "thiserror 2.0.18", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "reth-discv5" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "derive_more", + "discv5", + "enr", + "futures", + "itertools 0.14.0", + "metrics", + "rand 0.9.2", + "reth-chainspec", + "reth-ethereum-forks", + "reth-metrics", + "reth-network-peers", + "secp256k1 0.30.0", + "thiserror 2.0.18", + "tokio", + "tracing", +] + +[[package]] +name = "reth-dns-discovery" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-primitives", + "dashmap", + "data-encoding", + "enr", + "hickory-resolver", + "linked_hash_set", + "reth-ethereum-forks", + "reth-network-peers", + "reth-tokio-util", + "schnellru", + "secp256k1 0.30.0", + "serde", + "serde_with", + "thiserror 2.0.18", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "reth-downloaders" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "async-compression", + "futures", + "futures-util", + "itertools 0.14.0", + "metrics", + "pin-project", + "rayon", + "reth-config", + "reth-consensus", + "reth-metrics", + "reth-network-p2p", + "reth-network-peers", + "reth-primitives-traits", + "reth-storage-api", + "reth-tasks", + "thiserror 2.0.18", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", +] + +[[package]] +name = "reth-ecies" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "aes", + "alloy-primitives", + "alloy-rlp", + "block-padding", + "byteorder", + "cipher", + "concat-kdf", + "ctr", + "digest 0.10.7", + "futures", + "hmac", + "pin-project", + "rand 0.8.5", + "reth-network-peers", + "secp256k1 0.30.0", + "sha2", + "thiserror 2.0.18", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", +] + +[[package]] +name = "reth-engine-local" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-primitives", + "alloy-rpc-types-engine", + "eyre", + "futures-util", + "reth-chainspec", + "reth-engine-primitives", + "reth-ethereum-engine-primitives", + "reth-payload-builder", + "reth-payload-primitives", + "reth-primitives-traits", + "reth-storage-api", + "reth-transaction-pool", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "reth-engine-primitives" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rpc-types-engine", + "auto_impl", + "futures", + "reth-chain-state", + "reth-errors", + "reth-ethereum-primitives", + "reth-evm", + "reth-execution-types", + "reth-payload-builder-primitives", + "reth-payload-primitives", + "reth-primitives-traits", + "reth-trie-common", + "serde", + "thiserror 2.0.18", + "tokio", +] + +[[package]] +name = "reth-engine-service" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "futures", + "pin-project", + "reth-chainspec", + "reth-consensus", + "reth-engine-primitives", + "reth-engine-tree", + "reth-evm", + "reth-network-p2p", + "reth-node-types", + "reth-payload-builder", + "reth-provider", + "reth-prune", + "reth-stages-api", + "reth-tasks", + "reth-trie-db", +] + +[[package]] +name = "reth-engine-tree" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-eip7928", + "alloy-eips", + "alloy-evm", + "alloy-primitives", + "alloy-rlp", + "alloy-rpc-types-engine", + "crossbeam-channel", + "derive_more", + "fixed-cache", + "futures", + "metrics", + "moka", + "parking_lot", + "rayon", + "reth-chain-state", + "reth-consensus", + "reth-db", + "reth-engine-primitives", + "reth-errors", + "reth-ethereum-primitives", + "reth-evm", + "reth-execution-types", + "reth-metrics", + "reth-network-p2p", + "reth-payload-builder", + "reth-payload-primitives", + "reth-primitives-traits", + "reth-provider", + "reth-prune", + "reth-revm", + "reth-stages-api", + "reth-tasks", + "reth-trie", + "reth-trie-common", + "reth-trie-db", + "reth-trie-parallel", + "reth-trie-sparse", + "revm", + "revm-primitives", + "schnellru", + "smallvec", + "thiserror 2.0.18", + "tokio", + "tracing", +] + +[[package]] +name = "reth-engine-util" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-rpc-types-engine", + "eyre", + "futures", + "itertools 0.14.0", + "pin-project", + "reth-chainspec", + "reth-engine-primitives", + "reth-engine-tree", + "reth-errors", + "reth-evm", + "reth-fs-util", + "reth-payload-primitives", + "reth-primitives-traits", + "reth-revm", + "reth-storage-api", + "serde", + "serde_json", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "reth-era" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "ethereum_ssz 0.10.1", + "ethereum_ssz_derive 0.10.1", + "snap", + "thiserror 2.0.18", +] + +[[package]] +name = "reth-era-downloader" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-primitives", + "bytes", + "eyre", + "futures-util", + "reqwest", + "reth-era", + "reth-fs-util", + "sha2", + "tokio", +] + +[[package]] +name = "reth-era-utils" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-primitives", + "eyre", + "futures-util", + "reth-db-api", + "reth-era", + "reth-era-downloader", + "reth-etl", + "reth-fs-util", + "reth-primitives-traits", + "reth-provider", + "reth-stages-types", + "reth-storage-api", + "tokio", + "tracing", +] + +[[package]] +name = "reth-erc8004-indexer" +version = "0.1.0" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "axum", + "base64 0.22.1", + "clap", + "eyre", + "futures", + "http-body-util", + "reqwest", + "reth", + "reth-ethereum-cli", + "reth-execution-types", + "reth-exex", + "reth-node-api", + "reth-node-ethereum", + "reth-provider", + "reth-tracing", + "rusqlite", + "serde", + "serde_json", + "tempfile", + "time", + "tokio", + "tower", +] + +[[package]] +name = "reth-errors" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "reth-consensus", + "reth-execution-errors", + "reth-storage-errors", + "thiserror 2.0.18", +] + +[[package]] +name = "reth-eth-wire" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-chains", + "alloy-primitives", + "alloy-rlp", + "bytes", + "derive_more", + "futures", + "pin-project", + "reth-codecs", + "reth-ecies", + "reth-eth-wire-types", + "reth-ethereum-forks", + "reth-metrics", + "reth-network-peers", + "reth-primitives-traits", + "serde", + "snap", + "thiserror 2.0.18", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", +] + +[[package]] +name = "reth-eth-wire-types" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-chains", + "alloy-consensus", + "alloy-eips", + "alloy-hardforks", + "alloy-primitives", + "alloy-rlp", + "bytes", + "derive_more", + "reth-chainspec", + "reth-codecs-derive", + "reth-ethereum-primitives", + "reth-primitives-traits", + "serde", + "thiserror 2.0.18", +] + +[[package]] +name = "reth-ethereum-cli" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "clap", + "eyre", + "reth-chainspec", + "reth-cli", + "reth-cli-commands", + "reth-cli-runner", + "reth-db", + "reth-node-api", + "reth-node-builder", + "reth-node-core", + "reth-node-ethereum", + "reth-node-metrics", + "reth-rpc-server-types", + "reth-tasks", + "reth-tracing", + "tracing", +] + +[[package]] +name = "reth-ethereum-consensus" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "reth-chainspec", + "reth-consensus", + "reth-consensus-common", + "reth-execution-types", + "reth-primitives-traits", + "tracing", +] + +[[package]] +name = "reth-ethereum-engine-primitives" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-rpc-types-engine", + "reth-engine-primitives", + "reth-ethereum-primitives", + "reth-payload-primitives", + "reth-primitives-traits", + "serde", + "sha2", + "thiserror 2.0.18", +] + +[[package]] +name = "reth-ethereum-forks" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-eip2124", + "alloy-hardforks", + "alloy-primitives", + "auto_impl", + "once_cell", + "rustc-hash", +] + +[[package]] +name = "reth-ethereum-payload-builder" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-rpc-types-engine", + "reth-basic-payload-builder", + "reth-chainspec", + "reth-consensus-common", + "reth-errors", + "reth-ethereum-primitives", + "reth-evm", + "reth-evm-ethereum", + "reth-payload-builder", + "reth-payload-builder-primitives", + "reth-payload-primitives", + "reth-payload-validator", + "reth-primitives-traits", + "reth-revm", + "reth-storage-api", + "reth-transaction-pool", + "revm", + "tracing", +] + +[[package]] +name = "reth-ethereum-primitives" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-rpc-types-eth", + "alloy-serde", + "modular-bitfield", + "reth-codecs", + "reth-primitives-traits", + "reth-zstd-compressors", + "serde", + "serde_with", +] + +[[package]] +name = "reth-etl" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "rayon", + "reth-db-api", + "tempfile", +] + +[[package]] +name = "reth-evm" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-evm", + "alloy-primitives", + "auto_impl", + "derive_more", + "futures-util", + "metrics", + "rayon", + "reth-execution-errors", + "reth-execution-types", + "reth-metrics", + "reth-primitives-traits", + "reth-storage-api", + "reth-storage-errors", + "reth-trie-common", + "revm", +] + +[[package]] +name = "reth-evm-ethereum" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-evm", + "alloy-primitives", + "alloy-rpc-types-engine", + "derive_more", + "reth-chainspec", + "reth-ethereum-forks", + "reth-ethereum-primitives", + "reth-evm", + "reth-execution-types", + "reth-primitives-traits", + "reth-storage-errors", + "revm", +] + +[[package]] +name = "reth-execution-errors" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-evm", + "alloy-primitives", + "alloy-rlp", + "nybbles", + "reth-storage-errors", + "thiserror 2.0.18", +] + +[[package]] +name = "reth-execution-types" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-evm", + "alloy-primitives", + "derive_more", + "reth-ethereum-primitives", + "reth-primitives-traits", + "reth-trie-common", + "revm", + "serde", + "serde_with", +] + +[[package]] +name = "reth-exex" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "eyre", + "futures", + "itertools 0.14.0", + "metrics", + "parking_lot", + "reth-chain-state", + "reth-chainspec", + "reth-config", + "reth-ethereum-primitives", + "reth-evm", + "reth-exex-types", + "reth-fs-util", + "reth-metrics", + "reth-node-api", + "reth-node-core", + "reth-payload-builder", + "reth-primitives-traits", + "reth-provider", + "reth-prune-types", + "reth-revm", + "reth-stages-api", + "reth-tasks", + "reth-tracing", + "rmp-serde", + "thiserror 2.0.18", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "reth-exex-types" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "reth-chain-state", + "reth-execution-types", + "reth-primitives-traits", + "serde", + "serde_with", +] + +[[package]] +name = "reth-fs-util" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "serde", + "serde_json", + "thiserror 2.0.18", +] + +[[package]] +name = "reth-invalid-block-hooks" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-primitives", + "alloy-rlp", + "alloy-rpc-types-debug", + "eyre", + "futures", + "jsonrpsee", + "pretty_assertions", + "reth-engine-primitives", + "reth-evm", + "reth-primitives-traits", + "reth-provider", + "reth-revm", + "reth-rpc-api", + "reth-tracing", + "reth-trie", + "revm", + "revm-bytecode", + "revm-database", + "serde", + "serde_json", +] + +[[package]] +name = "reth-ipc" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "bytes", + "futures", + "futures-util", + "interprocess", + "jsonrpsee", + "pin-project", + "serde_json", + "thiserror 2.0.18", + "tokio", + "tokio-stream", + "tokio-util", + "tower", + "tracing", +] + +[[package]] +name = "reth-libmdbx" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "bitflags 2.11.0", + "byteorder", + "dashmap", + "derive_more", + "parking_lot", + "reth-mdbx-sys", + "smallvec", + "thiserror 2.0.18", + "tracing", +] + +[[package]] +name = "reth-mdbx-sys" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "bindgen", + "cc", +] + +[[package]] +name = "reth-metrics" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "futures", + "metrics", + "metrics-derive", + "tokio", + "tokio-util", +] + +[[package]] +name = "reth-net-banlist" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-primitives", + "ipnet", +] + +[[package]] +name = "reth-net-nat" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "futures-util", + "if-addrs", + "reqwest", + "serde_with", + "thiserror 2.0.18", + "tokio", + "tracing", +] + +[[package]] +name = "reth-network" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "aquamarine", + "auto_impl", + "derive_more", + "discv5", + "enr", + "futures", + "itertools 0.14.0", + "metrics", + "parking_lot", + "pin-project", + "rand 0.8.5", + "rand 0.9.2", + "rayon", + "reth-chainspec", + "reth-consensus", + "reth-discv4", + "reth-discv5", + "reth-dns-discovery", + "reth-ecies", + "reth-eth-wire", + "reth-eth-wire-types", + "reth-ethereum-forks", + "reth-ethereum-primitives", + "reth-fs-util", + "reth-metrics", + "reth-net-banlist", + "reth-network-api", + "reth-network-p2p", + "reth-network-peers", + "reth-network-types", + "reth-primitives-traits", + "reth-storage-api", + "reth-tasks", + "reth-tokio-util", + "reth-transaction-pool", + "rustc-hash", + "schnellru", + "secp256k1 0.30.0", + "serde", + "smallvec", + "thiserror 2.0.18", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", +] + +[[package]] +name = "reth-network-api" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-primitives", + "alloy-rpc-types-admin", + "alloy-rpc-types-eth", + "auto_impl", + "derive_more", + "enr", + "futures", + "reth-eth-wire-types", + "reth-ethereum-forks", + "reth-network-p2p", + "reth-network-peers", + "reth-network-types", + "reth-tokio-util", + "serde", + "thiserror 2.0.18", + "tokio", + "tokio-stream", +] + +[[package]] +name = "reth-network-p2p" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "auto_impl", + "derive_more", + "futures", + "reth-consensus", + "reth-eth-wire-types", + "reth-ethereum-primitives", + "reth-network-peers", + "reth-network-types", + "reth-primitives-traits", + "reth-storage-errors", + "tokio", + "tracing", +] + +[[package]] +name = "reth-network-peers" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "enr", + "secp256k1 0.30.0", + "serde_with", + "thiserror 2.0.18", + "tokio", + "url", +] + +[[package]] +name = "reth-network-types" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-eip2124", + "humantime-serde", + "reth-net-banlist", + "reth-network-peers", + "serde", + "serde_json", + "tracing", +] + +[[package]] +name = "reth-nippy-jar" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "anyhow", + "bincode", + "derive_more", + "lz4_flex", + "memmap2", + "reth-fs-util", + "serde", + "thiserror 2.0.18", + "tracing", + "zstd", +] + +[[package]] +name = "reth-node-api" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-rpc-types-engine", + "eyre", + "reth-basic-payload-builder", + "reth-consensus", + "reth-db-api", + "reth-engine-primitives", + "reth-evm", + "reth-network-api", + "reth-node-core", + "reth-node-types", + "reth-payload-builder", + "reth-payload-builder-primitives", + "reth-payload-primitives", + "reth-provider", + "reth-tasks", + "reth-tokio-util", + "reth-transaction-pool", +] + +[[package]] +name = "reth-node-builder" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-provider", + "alloy-rpc-types", + "alloy-rpc-types-engine", + "aquamarine", + "eyre", + "fdlimit", + "futures", + "jsonrpsee", + "parking_lot", + "rayon", + "reth-basic-payload-builder", + "reth-chain-state", + "reth-chainspec", + "reth-config", + "reth-consensus", + "reth-consensus-debug-client", + "reth-db", + "reth-db-api", + "reth-db-common", + "reth-downloaders", + "reth-engine-local", + "reth-engine-primitives", + "reth-engine-service", + "reth-engine-tree", + "reth-engine-util", + "reth-evm", + "reth-exex", + "reth-fs-util", + "reth-invalid-block-hooks", + "reth-network", + "reth-network-api", + "reth-network-p2p", + "reth-node-api", + "reth-node-core", + "reth-node-ethstats", + "reth-node-events", + "reth-node-metrics", + "reth-payload-builder", + "reth-primitives-traits", + "reth-provider", + "reth-prune", + "reth-rpc", + "reth-rpc-api", + "reth-rpc-builder", + "reth-rpc-engine-api", + "reth-rpc-eth-types", + "reth-rpc-layer", + "reth-stages", + "reth-static-file", + "reth-tasks", + "reth-tokio-util", + "reth-tracing", + "reth-transaction-pool", + "reth-trie-db", + "secp256k1 0.30.0", + "serde_json", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "reth-node-core" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rpc-types-engine", + "clap", + "derive_more", + "dirs-next", + "eyre", + "futures", + "humantime", + "ipnet", + "rand 0.9.2", + "reth-chainspec", + "reth-cli-util", + "reth-config", + "reth-consensus", + "reth-db", + "reth-discv4", + "reth-discv5", + "reth-engine-local", + "reth-engine-primitives", + "reth-ethereum-forks", + "reth-net-banlist", + "reth-net-nat", + "reth-network", + "reth-network-p2p", + "reth-network-peers", + "reth-primitives-traits", + "reth-prune-types", + "reth-rpc-convert", + "reth-rpc-eth-types", + "reth-rpc-server-types", + "reth-stages-types", + "reth-storage-api", + "reth-storage-errors", + "reth-tracing", + "reth-tracing-otlp", + "reth-transaction-pool", + "secp256k1 0.30.0", + "serde", + "shellexpand", + "strum", + "thiserror 2.0.18", + "toml", + "tracing", + "url", + "vergen", + "vergen-git2", +] + +[[package]] +name = "reth-node-ethereum" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-eips", + "alloy-network", + "alloy-rpc-types-engine", + "alloy-rpc-types-eth", + "eyre", + "reth-chainspec", + "reth-engine-local", + "reth-engine-primitives", + "reth-ethereum-consensus", + "reth-ethereum-engine-primitives", + "reth-ethereum-payload-builder", + "reth-ethereum-primitives", + "reth-evm", + "reth-evm-ethereum", + "reth-network", + "reth-node-api", + "reth-node-builder", + "reth-payload-primitives", + "reth-primitives-traits", + "reth-provider", + "reth-revm", + "reth-rpc", + "reth-rpc-api", + "reth-rpc-builder", + "reth-rpc-eth-api", + "reth-rpc-eth-types", + "reth-rpc-server-types", + "reth-tracing", + "reth-transaction-pool", + "revm", + "tokio", +] + +[[package]] +name = "reth-node-ethstats" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-primitives", + "chrono", + "futures-util", + "reth-chain-state", + "reth-network-api", + "reth-primitives-traits", + "reth-storage-api", + "reth-transaction-pool", + "serde", + "serde_json", + "thiserror 2.0.18", + "tokio", + "tokio-stream", + "tokio-tungstenite 0.28.0", + "tracing", + "url", +] + +[[package]] +name = "reth-node-events" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rpc-types-engine", + "derive_more", + "futures", + "humantime", + "pin-project", + "reth-engine-primitives", + "reth-network-api", + "reth-primitives-traits", + "reth-prune-types", + "reth-stages", + "reth-static-file-types", + "reth-storage-api", + "tokio", + "tracing", +] + +[[package]] +name = "reth-node-metrics" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "bytes", + "eyre", + "http", + "http-body-util", + "jsonrpsee-server", + "metrics", + "metrics-exporter-prometheus", + "metrics-process", + "metrics-util", + "procfs", + "reqwest", + "reth-metrics", + "reth-tasks", + "tikv-jemalloc-ctl", + "tokio", + "tower", + "tracing", +] + +[[package]] +name = "reth-node-types" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "reth-chainspec", + "reth-db-api", + "reth-engine-primitives", + "reth-payload-primitives", + "reth-primitives-traits", +] + +[[package]] +name = "reth-payload-builder" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-primitives", + "alloy-rpc-types", + "futures-util", + "metrics", + "reth-chain-state", + "reth-ethereum-engine-primitives", + "reth-metrics", + "reth-payload-builder-primitives", + "reth-payload-primitives", + "reth-primitives-traits", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "reth-payload-builder-primitives" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "pin-project", + "reth-payload-primitives", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "reth-payload-primitives" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rpc-types-engine", + "auto_impl", + "either", + "op-alloy-rpc-types-engine", + "reth-chain-state", + "reth-chainspec", + "reth-errors", + "reth-execution-types", + "reth-primitives-traits", + "reth-trie-common", + "serde", + "thiserror 2.0.18", + "tokio", +] + +[[package]] +name = "reth-payload-validator" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-rpc-types-engine", + "reth-primitives-traits", +] + +[[package]] +name = "reth-primitives" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-primitives", + "once_cell", + "reth-ethereum-forks", + "reth-ethereum-primitives", + "reth-primitives-traits", + "reth-static-file-types", +] + +[[package]] +name = "reth-primitives-traits" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-genesis", + "alloy-primitives", + "alloy-rlp", + "alloy-rpc-types-eth", + "alloy-trie", + "auto_impl", + "byteorder", + "bytes", + "dashmap", + "derive_more", + "modular-bitfield", + "once_cell", + "op-alloy-consensus", + "rayon", + "reth-codecs", + "revm-bytecode", + "revm-primitives", + "revm-state", + "secp256k1 0.30.0", + "serde", + "serde_with", + "thiserror 2.0.18", +] + +[[package]] +name = "reth-provider" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rpc-types-engine", + "eyre", + "itertools 0.14.0", + "metrics", + "notify", + "parking_lot", + "rayon", + "reth-chain-state", + "reth-chainspec", + "reth-codecs", + "reth-db", + "reth-db-api", + "reth-errors", + "reth-ethereum-primitives", + "reth-execution-types", + "reth-fs-util", + "reth-metrics", + "reth-nippy-jar", + "reth-node-types", + "reth-primitives-traits", + "reth-prune-types", + "reth-stages-types", + "reth-static-file-types", + "reth-storage-api", + "reth-storage-errors", + "reth-tasks", + "reth-trie", + "reth-trie-db", + "revm-database", + "rocksdb", + "strum", + "tracing", +] + +[[package]] +name = "reth-prune" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "itertools 0.14.0", + "metrics", + "rayon", + "reth-config", + "reth-db-api", + "reth-errors", + "reth-exex-types", + "reth-metrics", + "reth-primitives-traits", + "reth-provider", + "reth-prune-types", + "reth-stages-types", + "reth-static-file-types", + "reth-storage-api", + "reth-tokio-util", + "rustc-hash", + "thiserror 2.0.18", + "tokio", + "tracing", +] + +[[package]] +name = "reth-prune-types" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-primitives", + "derive_more", + "modular-bitfield", + "reth-codecs", + "serde", + "strum", + "thiserror 2.0.18", + "tracing", +] + +[[package]] +name = "reth-revm" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-primitives", + "reth-primitives-traits", + "reth-storage-api", + "reth-storage-errors", + "reth-trie", + "revm", +] + +[[package]] +name = "reth-rpc" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-dyn-abi", + "alloy-eip7928", + "alloy-eips", + "alloy-evm", + "alloy-genesis", + "alloy-network", + "alloy-primitives", + "alloy-rlp", + "alloy-rpc-client", + "alloy-rpc-types", + "alloy-rpc-types-admin", + "alloy-rpc-types-beacon", + "alloy-rpc-types-debug", + "alloy-rpc-types-engine", + "alloy-rpc-types-eth", + "alloy-rpc-types-mev", + "alloy-rpc-types-trace", + "alloy-rpc-types-txpool", + "alloy-serde", + "alloy-signer", + "alloy-signer-local", + "async-trait", + "derive_more", + "dyn-clone", + "futures", + "itertools 0.14.0", + "jsonrpsee", + "jsonrpsee-types", + "parking_lot", + "pin-project", + "reth-chain-state", + "reth-chainspec", + "reth-consensus", + "reth-consensus-common", + "reth-engine-primitives", + "reth-errors", + "reth-ethereum-engine-primitives", + "reth-ethereum-primitives", + "reth-evm", + "reth-evm-ethereum", + "reth-execution-types", + "reth-metrics", + "reth-network-api", + "reth-network-peers", + "reth-network-types", + "reth-node-api", + "reth-primitives-traits", + "reth-revm", + "reth-rpc-api", + "reth-rpc-convert", + "reth-rpc-engine-api", + "reth-rpc-eth-api", + "reth-rpc-eth-types", + "reth-rpc-server-types", + "reth-storage-api", + "reth-tasks", + "reth-transaction-pool", + "reth-trie-common", + "revm", + "revm-inspectors", + "revm-primitives", + "serde", + "serde_json", + "sha2", + "thiserror 2.0.18", + "tokio", + "tokio-stream", + "tracing", + "tracing-futures", +] + +[[package]] +name = "reth-rpc-api" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-eip7928", + "alloy-eips", + "alloy-genesis", + "alloy-json-rpc", + "alloy-primitives", + "alloy-rpc-types", + "alloy-rpc-types-admin", + "alloy-rpc-types-anvil", + "alloy-rpc-types-beacon", + "alloy-rpc-types-debug", + "alloy-rpc-types-engine", + "alloy-rpc-types-eth", + "alloy-rpc-types-mev", + "alloy-rpc-types-trace", + "alloy-rpc-types-txpool", + "alloy-serde", + "jsonrpsee", + "reth-chain-state", + "reth-engine-primitives", + "reth-network-peers", + "reth-rpc-eth-api", + "reth-trie-common", + "serde_json", +] + +[[package]] +name = "reth-rpc-builder" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-network", + "alloy-provider", + "dyn-clone", + "http", + "jsonrpsee", + "metrics", + "pin-project", + "reth-chain-state", + "reth-chainspec", + "reth-consensus", + "reth-engine-primitives", + "reth-evm", + "reth-ipc", + "reth-metrics", + "reth-network-api", + "reth-node-core", + "reth-primitives-traits", + "reth-rpc", + "reth-rpc-api", + "reth-rpc-eth-api", + "reth-rpc-eth-types", + "reth-rpc-layer", + "reth-rpc-server-types", + "reth-storage-api", + "reth-tasks", + "reth-tokio-util", + "reth-transaction-pool", + "serde", + "thiserror 2.0.18", + "tokio", + "tokio-util", + "tower", + "tower-http", + "tracing", +] + +[[package]] +name = "reth-rpc-convert" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-evm", + "alloy-json-rpc", + "alloy-network", + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-signer", + "auto_impl", + "dyn-clone", + "jsonrpsee-types", + "reth-ethereum-primitives", + "reth-evm", + "reth-primitives-traits", + "thiserror 2.0.18", +] + +[[package]] +name = "reth-rpc-engine-api" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rpc-types-engine", + "async-trait", + "jsonrpsee-core", + "jsonrpsee-types", + "metrics", + "reth-chainspec", + "reth-engine-primitives", + "reth-metrics", + "reth-network-api", + "reth-payload-builder", + "reth-payload-builder-primitives", + "reth-payload-primitives", + "reth-primitives-traits", + "reth-rpc-api", + "reth-storage-api", + "reth-tasks", + "reth-transaction-pool", + "serde", + "thiserror 2.0.18", + "tokio", + "tracing", +] + +[[package]] +name = "reth-rpc-eth-api" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-dyn-abi", + "alloy-eips", + "alloy-evm", + "alloy-json-rpc", + "alloy-network", + "alloy-primitives", + "alloy-rlp", + "alloy-rpc-types-eth", + "alloy-rpc-types-mev", + "alloy-serde", + "async-trait", + "auto_impl", + "dyn-clone", + "futures", + "jsonrpsee", + "jsonrpsee-types", + "parking_lot", + "reth-chain-state", + "reth-chainspec", + "reth-errors", + "reth-evm", + "reth-network-api", + "reth-node-api", + "reth-primitives-traits", + "reth-revm", + "reth-rpc-convert", + "reth-rpc-eth-types", + "reth-rpc-server-types", + "reth-storage-api", + "reth-tasks", + "reth-transaction-pool", + "reth-trie-common", + "revm", + "revm-inspectors", + "tokio", + "tracing", +] + +[[package]] +name = "reth-rpc-eth-types" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-evm", + "alloy-network", + "alloy-primitives", + "alloy-rpc-client", + "alloy-rpc-types-eth", + "alloy-sol-types", + "alloy-transport", + "derive_more", + "futures", + "itertools 0.14.0", + "jsonrpsee-core", + "jsonrpsee-types", + "metrics", + "rand 0.9.2", + "reqwest", + "reth-chain-state", + "reth-chainspec", + "reth-errors", + "reth-ethereum-primitives", + "reth-evm", + "reth-execution-types", + "reth-metrics", + "reth-primitives-traits", + "reth-revm", + "reth-rpc-convert", + "reth-rpc-server-types", + "reth-storage-api", + "reth-tasks", + "reth-transaction-pool", + "reth-trie", + "revm", + "revm-inspectors", + "schnellru", + "serde", + "thiserror 2.0.18", + "tokio", + "tokio-stream", + "tracing", + "url", +] + +[[package]] +name = "reth-rpc-layer" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-rpc-types-engine", + "http", + "jsonrpsee-http-client", + "pin-project", + "tower", + "tower-http", + "tracing", +] + +[[package]] +name = "reth-rpc-server-types" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rpc-types-engine", + "jsonrpsee-core", + "jsonrpsee-types", + "reth-errors", + "reth-network-api", + "serde", + "strum", +] + +[[package]] +name = "reth-stages" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "bincode", + "eyre", + "futures-util", + "itertools 0.14.0", + "num-traits", + "rayon", + "reqwest", + "reth-codecs", + "reth-config", + "reth-consensus", + "reth-db", + "reth-db-api", + "reth-era", + "reth-era-downloader", + "reth-era-utils", + "reth-etl", + "reth-evm", + "reth-execution-types", + "reth-exex", + "reth-fs-util", + "reth-network-p2p", + "reth-primitives-traits", + "reth-provider", + "reth-prune", + "reth-prune-types", + "reth-revm", + "reth-stages-api", + "reth-static-file-types", + "reth-storage-api", + "reth-storage-errors", + "reth-tasks", + "reth-trie", + "reth-trie-db", + "thiserror 2.0.18", + "tokio", + "tracing", +] + +[[package]] +name = "reth-stages-api" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "aquamarine", + "auto_impl", + "futures-util", + "metrics", + "reth-consensus", + "reth-errors", + "reth-metrics", + "reth-network-p2p", + "reth-primitives-traits", + "reth-provider", + "reth-prune", + "reth-stages-types", + "reth-static-file", + "reth-static-file-types", + "reth-tokio-util", + "thiserror 2.0.18", + "tokio", + "tracing", +] + +[[package]] +name = "reth-stages-types" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-primitives", + "bytes", + "modular-bitfield", + "reth-codecs", + "reth-trie-common", + "serde", +] + +[[package]] +name = "reth-static-file" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-primitives", + "parking_lot", + "rayon", + "reth-codecs", + "reth-db-api", + "reth-primitives-traits", + "reth-provider", + "reth-prune-types", + "reth-stages-types", + "reth-static-file-types", + "reth-storage-errors", + "reth-tokio-util", + "tracing", +] + +[[package]] +name = "reth-static-file-types" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-primitives", + "clap", + "derive_more", + "fixed-map", + "reth-stages-types", + "serde", + "strum", + "tracing", +] + +[[package]] +name = "reth-storage-api" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rpc-types-engine", + "auto_impl", + "reth-chainspec", + "reth-db-api", + "reth-db-models", + "reth-ethereum-primitives", + "reth-execution-types", + "reth-primitives-traits", + "reth-prune-types", + "reth-stages-types", + "reth-storage-errors", + "reth-trie-common", + "revm-database", + "serde_json", +] + +[[package]] +name = "reth-storage-errors" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "derive_more", + "reth-primitives-traits", + "reth-prune-types", + "reth-static-file-types", + "revm-database-interface", + "revm-state", + "thiserror 2.0.18", +] + +[[package]] +name = "reth-tasks" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "auto_impl", + "dyn-clone", + "futures-util", + "metrics", + "pin-project", + "rayon", + "reth-metrics", + "thiserror 2.0.18", + "tokio", + "tracing", + "tracing-futures", +] + +[[package]] +name = "reth-tokio-util" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "reth-tracing" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "clap", + "eyre", + "reth-tracing-otlp", + "rolling-file", + "tracing", + "tracing-appender", + "tracing-journald", + "tracing-logfmt", + "tracing-samply", + "tracing-subscriber 0.3.23", +] + +[[package]] +name = "reth-tracing-otlp" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "clap", + "eyre", + "opentelemetry", + "opentelemetry-appender-tracing", + "opentelemetry-otlp", + "opentelemetry-semantic-conventions", + "opentelemetry_sdk", + "tracing", + "tracing-opentelemetry", + "tracing-subscriber 0.3.23", + "url", +] + +[[package]] +name = "reth-transaction-pool" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "aquamarine", + "auto_impl", + "bitflags 2.11.0", + "futures-util", + "metrics", + "parking_lot", + "pin-project", + "rand 0.9.2", + "reth-chain-state", + "reth-chainspec", + "reth-eth-wire-types", + "reth-ethereum-primitives", + "reth-evm", + "reth-evm-ethereum", + "reth-execution-types", + "reth-fs-util", + "reth-metrics", + "reth-primitives-traits", + "reth-storage-api", + "reth-tasks", + "revm", + "revm-interpreter", + "revm-primitives", + "rustc-hash", + "schnellru", + "serde", + "serde_json", + "smallvec", + "thiserror 2.0.18", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "reth-trie" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-trie", + "auto_impl", + "itertools 0.14.0", + "metrics", + "parking_lot", + "reth-execution-errors", + "reth-metrics", + "reth-primitives-traits", + "reth-stages-types", + "reth-storage-errors", + "reth-trie-common", + "reth-trie-sparse", + "revm-database", + "tracing", +] + +[[package]] +name = "reth-trie-common" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-consensus", + "alloy-primitives", + "alloy-rlp", + "alloy-rpc-types-eth", + "alloy-serde", + "alloy-trie", + "arrayvec", + "bytes", + "derive_more", + "itertools 0.14.0", + "nybbles", + "rayon", + "reth-codecs", + "reth-primitives-traits", + "revm-database", + "serde", + "serde_with", +] + +[[package]] +name = "reth-trie-db" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-primitives", + "metrics", + "parking_lot", + "reth-db-api", + "reth-execution-errors", + "reth-metrics", + "reth-primitives-traits", + "reth-stages-types", + "reth-storage-api", + "reth-storage-errors", + "reth-trie", + "reth-trie-common", + "tracing", +] + +[[package]] +name = "reth-trie-parallel" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "crossbeam-channel", + "derive_more", + "itertools 0.14.0", + "metrics", + "rayon", + "reth-execution-errors", + "reth-metrics", + "reth-primitives-traits", + "reth-provider", + "reth-storage-errors", + "reth-tasks", + "reth-trie", + "reth-trie-common", + "reth-trie-sparse", + "thiserror 2.0.18", + "tracing", +] + +[[package]] +name = "reth-trie-sparse" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "alloy-trie", + "auto_impl", + "metrics", + "rayon", + "reth-execution-errors", + "reth-metrics", + "reth-primitives-traits", + "reth-trie-common", + "smallvec", + "tracing", +] + +[[package]] +name = "reth-zstd-compressors" +version = "1.11.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.1#bef3d7b4d1da937fcccc9bbd6f8bd93e16380dc7" +dependencies = [ + "zstd", +] + +[[package]] +name = "revm" +version = "34.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2aabdebaa535b3575231a88d72b642897ae8106cf6b0d12eafc6bfdf50abfc7" +dependencies = [ + "revm-bytecode", + "revm-context", + "revm-context-interface", + "revm-database", + "revm-database-interface", + "revm-handler", + "revm-inspector", + "revm-interpreter", + "revm-precompile", + "revm-primitives", + "revm-state", +] + +[[package]] +name = "revm-bytecode" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d1e5c1eaa44d39d537f668bc5c3409dc01e5c8be954da6c83370bbdf006457" +dependencies = [ + "bitvec", + "phf", + "revm-primitives", + "serde", +] + +[[package]] +name = "revm-context" +version = "13.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "892ff3e6a566cf8d72ffb627fdced3becebbd9ba64089c25975b9b028af326a5" +dependencies = [ + "bitvec", + "cfg-if", + "derive-where", + "revm-bytecode", + "revm-context-interface", + "revm-database-interface", + "revm-primitives", + "revm-state", + "serde", +] + +[[package]] +name = "revm-context-interface" +version = "14.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57f61cc6d23678c4840af895b19f8acfbbd546142ec8028b6526c53cc1c16c98" +dependencies = [ + "alloy-eip2930", + "alloy-eip7702", + "auto_impl", + "either", + "revm-database-interface", + "revm-primitives", + "revm-state", + "serde", +] + +[[package]] +name = "revm-database" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "529528d0b05fe646be86223032c3e77aa8b05caa2a35447d538c55965956a511" +dependencies = [ + "alloy-eips", + "revm-bytecode", + "revm-database-interface", + "revm-primitives", + "revm-state", + "serde", +] + +[[package]] +name = "revm-database-interface" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7bf93ac5b91347c057610c0d96e923db8c62807e03f036762d03e981feddc1d" +dependencies = [ + "auto_impl", + "either", + "revm-primitives", + "revm-state", + "serde", + "thiserror 2.0.18", +] + +[[package]] +name = "revm-handler" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cd0e43e815a85eded249df886c4badec869195e70cdd808a13cfca2794622d2" +dependencies = [ + "auto_impl", + "derive-where", + "revm-bytecode", + "revm-context", + "revm-context-interface", + "revm-database-interface", + "revm-interpreter", + "revm-precompile", + "revm-primitives", + "revm-state", + "serde", +] + +[[package]] +name = "revm-inspector" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f3ccad59db91ef93696536a0dbaf2f6f17cfe20d4d8843ae118edb7e97947ef" +dependencies = [ + "auto_impl", + "either", + "revm-context", + "revm-database-interface", + "revm-handler", + "revm-interpreter", + "revm-primitives", + "revm-state", + "serde", + "serde_json", +] + +[[package]] +name = "revm-inspectors" +version = "0.34.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e435414e9de50a1b930da602067c76365fea2fea11e80ceb50783c94ddd127f" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-rpc-types-trace", + "alloy-sol-types", + "anstyle", + "boa_engine", + "boa_gc", + "colorchoice", + "revm", + "serde", + "serde_json", + "thiserror 2.0.18", +] + +[[package]] +name = "revm-interpreter" +version = "32.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11406408597bc249392d39295831c4b641b3a6f5c471a7c41104a7a1e3564c07" +dependencies = [ + "revm-bytecode", + "revm-context-interface", + "revm-primitives", + "revm-state", + "serde", +] + +[[package]] +name = "revm-precompile" +version = "32.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2ec11f45deec71e4945e1809736bb20d454285f9167ab53c5159dae1deb603f" +dependencies = [ + "ark-bls12-381", + "ark-bn254", + "ark-ec", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", + "arrayref", + "aurora-engine-modexp", + "blst", + "c-kzg", + "cfg-if", + "k256", + "p256", + "revm-primitives", + "ripemd", + "secp256k1 0.31.1", + "sha2", +] + +[[package]] +name = "revm-primitives" +version = "22.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfb5ce6cf18b118932bcdb7da05cd9c250f2cb9f64131396b55f3fe3537c35" +dependencies = [ + "alloy-primitives", + "num_enum", + "once_cell", + "serde", +] + +[[package]] +name = "revm-state" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "311720d4f0f239b041375e7ddafdbd20032a33b7bae718562ea188e188ed9fd3" +dependencies = [ + "alloy-eip7928", + "bitflags 2.11.0", + "revm-bytecode", + "revm-primitives", + "serde", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "ringbuffer" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57b0b88a509053cbfd535726dcaaceee631313cef981266119527a1d110f6d2b" + +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "rlimit" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f35ee2729c56bb610f6dba436bf78135f728b7373bdffae2ec815b2d3eb98cc3" +dependencies = [ + "libc", +] + +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] +name = "rmp" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "rmp-serde" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155" +dependencies = [ + "rmp", + "serde", +] + +[[package]] +name = "roaring" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ba9ce64a8f45d7fc86358410bb1a82e8c987504c0d4900e9141d69a9f26c885" +dependencies = [ + "bytemuck", + "byteorder", +] + +[[package]] +name = "rocksdb" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddb7af00d2b17dbd07d82c0063e25411959748ff03e8d4f96134c2ff41fce34f" +dependencies = [ + "libc", + "librocksdb-sys", +] + +[[package]] +name = "rolling-file" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8395b4f860856b740f20a296ea2cd4d823e81a2658cf05ef61be22916026a906" +dependencies = [ + "chrono", +] + +[[package]] +name = "route-recognizer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" + +[[package]] +name = "ruint" +version = "1.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c141e807189ad38a07276942c6623032d3753c8859c146104ac2e4d68865945a" +dependencies = [ + "alloy-rlp", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "ark-ff 0.5.0", + "bytes", + "fastrlp 0.3.1", + "fastrlp 0.4.0", + "num-bigint", + "num-integer", + "num-traits", + "parity-scale-codec", + "primitive-types", + "proptest", + "rand 0.8.5", + "rand 0.9.2", + "rlp", + "ruint-macro", + "serde_core", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" + +[[package]] +name = "rusqlite" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f" +dependencies = [ + "bitflags 2.11.0", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink 0.10.0", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +dependencies = [ + "rand 0.8.5", +] + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver 0.9.0", +] + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver 1.0.27", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.11.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-platform-verifier" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19787cda76408ec5404443dc8b31795c87cd8fec49762dc75fa727740d34acc1" +dependencies = [ + "core-foundation", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs 0.26.11", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + +[[package]] +name = "rustls-webpki" +version = "0.103.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "rusty-fork" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "ryu-js" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd29631678d6fb0903b69223673e122c32e9ae559d0960a38d574695ebc0ea15" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schnellru" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "356285bbf17bea63d9e52e96bd18f039672ac92b55b8cb997d6162a2a37d1649" +dependencies = [ + "ahash", + "cfg-if", + "hashbrown 0.13.2", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "serdect", + "subtle", + "zeroize", +] + +[[package]] +name = "secp256k1" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" +dependencies = [ + "bitcoin_hashes", + "rand 0.8.5", + "secp256k1-sys 0.10.1", + "serde", +] + +[[package]] +name = "secp256k1" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c3c81b43dc2d8877c216a3fccf76677ee1ebccd429566d3e67447290d0c42b2" +dependencies = [ + "bitcoin_hashes", + "rand 0.9.2", + "secp256k1-sys 0.11.0", +] + +[[package]] +name = "secp256k1-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" +dependencies = [ + "cc", +] + +[[package]] +name = "secp256k1-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb913707158fadaf0d8702c2db0e857de66eb003ccfdda5924b5f5ac98efb38" +dependencies = [ + "cc", +] + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags 2.11.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser 0.7.0", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser 0.10.3", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "semver-parser" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9900206b54a3527fdc7b8a938bffd94a568bac4f4aa8113b209df75a09c0dec2" +dependencies = [ + "pest", +] + +[[package]] +name = "send_wrapper" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" + +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "indexmap 2.13.0", + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + +[[package]] +name = "serde_spanned" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd5414fad8e6907dbdd5bc441a50ae8d6e26151a03b1de04d89a5576de61d01f" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.13.0", + "schemars 0.9.0", + "schemars 1.2.1", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3db8978e608f1fe7357e211969fd9abdcae80bac1ba7a3369bb7eb6b404eb65" +dependencies = [ + "darling 0.23.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serdect" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" +dependencies = [ + "base16ct", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "sha3-asm" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b31139435f327c93c6038ed350ae4588e2c70a13d50599509fee6349967ba35a" +dependencies = [ + "cc", + "cfg-if", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shellexpand" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32824fab5e16e6c4d86dc1ba84489390419a39f97699852b66480bb87d297ed8" +dependencies = [ + "dirs", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + +[[package]] +name = "simple_asn1" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d585997b0ac10be3c5ee635f1bab02d512760d14b7c468801ac8a01d9ae5f1d" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror 2.0.18", + "time", +] + +[[package]] +name = "siphasher" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" + +[[package]] +name = "sketches-ddsketch" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6f73aeb92d671e0cc4dca167e59b2deb6387c375391bc99ee743f326994a2b" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "small_btree" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ba60d2df92ba73864714808ca68c059734853e6ab722b40e1cf543ebb3a057a" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +dependencies = [ + "serde", +] + +[[package]] +name = "snap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" + +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socket2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "soketto" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e859df029d160cb88608f5d7df7fb4753fd20fdfb4de5644f3d8b8440841721" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures", + "http", + "httparse", + "log", + "rand 0.8.5", + "sha1", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn-solidity" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53f425ae0b12e2f5ae65542e00898d500d4d318b4baf09f40fd0d410454e9947" +dependencies = [ + "paste", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "sysinfo" +version = "0.38.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ab6a2f8bfe508deb3c6406578252e491d299cbbf3bc0529ecc3313aee4a52f" +dependencies = [ + "libc", + "memchr", + "ntapi", + "objc2-core-foundation", + "objc2-io-kit", + "windows", +] + +[[package]] +name = "tag_ptr" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0e973b34477b7823833469eb0f5a3a60370fef7a453e02d751b59180d0a5a05" + +[[package]] +name = "tagptr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tar" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "thin-vec" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "144f754d318415ac792f9d69fc87abbbfc043ce2ef041c60f16ad828f638717d" + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "tikv-jemalloc-ctl" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "661f1f6a57b3a36dc9174a2c10f19513b4866816e13425d3e418b11cc37bc24c" +dependencies = [ + "libc", + "paste", + "tikv-jemalloc-sys", +] + +[[package]] +name = "tikv-jemalloc-sys" +version = "0.6.1+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd8aa5b2ab86a2cefa406d889139c162cbb230092f7d1d7cbc1716405d852a3b" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "tikv-jemallocator" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0359b4327f954e0567e69fb191cf1436617748813819c94b8cd4a431422d053a" +dependencies = [ + "libc", + "tikv-jemalloc-sys", +] + +[[package]] +name = "time" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +dependencies = [ + "deranged", + "itoa", + "js-sys", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "serde_core", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.6.3", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" +dependencies = [ + "futures-util", + "log", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tungstenite 0.26.2", + "webpki-roots 0.26.11", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite 0.28.0", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-io", + "futures-sink", + "pin-project-lite", + "slab", + "tokio", +] + +[[package]] +name = "toml" +version = "0.9.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" +dependencies = [ + "indexmap 2.13.0", + "serde_core", + "serde_spanned", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_datetime" +version = "1.0.0+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.25.4+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7193cbd0ce53dc966037f54351dbbcf0d5a642c7f0038c382ef9e677ce8c13f2" +dependencies = [ + "indexmap 2.13.0", + "toml_datetime 1.0.0+spec-1.1.0", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.9+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" + +[[package]] +name = "tonic" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fec7c61a0695dc1887c1b53952990f3ad2e3a31453e1f49f10e75424943a93ec" +dependencies = [ + "async-trait", + "base64 0.22.1", + "bytes", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "sync_wrapper", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic-prost" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a55376a0bbaa4975a3f10d009ad763d8f4108f067c7c2e74f3001fb49778d309" +dependencies = [ + "bytes", + "prost", + "tonic", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "hdrhistogram", + "indexmap 2.13.0", + "pin-project-lite", + "slab", + "sync_wrapper", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "async-compression", + "base64 0.22.1", + "bitflags 2.11.0", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "http-range-header", + "httpdate", + "iri-string", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "tokio", + "tokio-util", + "tower", + "tower-layer", + "tower-service", + "tracing", + "uuid", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-appender" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "786d480bce6247ab75f005b14ae1624ad978d3029d9113f0a22fa1ac773faeaf" +dependencies = [ + "crossbeam-channel", + "thiserror 2.0.18", + "time", + "tracing-subscriber 0.3.23", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "tracing-journald" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3a81ed245bfb62592b1e2bc153e77656d94ee6a0497683a65a12ccaf2438d0" +dependencies = [ + "libc", + "tracing-core", + "tracing-subscriber 0.3.23", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-logfmt" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1f47d22deb79c3f59fcf2a1f00f60cbdc05462bf17d1cd356c1fefa3f444bd" +dependencies = [ + "time", + "tracing", + "tracing-core", + "tracing-subscriber 0.3.23", +] + +[[package]] +name = "tracing-opentelemetry" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ac28f2d093c6c477eaa76b23525478f38de514fa9aeb1285738d4b97a9552fc" +dependencies = [ + "js-sys", + "opentelemetry", + "smallvec", + "tracing", + "tracing-core", + "tracing-log", + "tracing-subscriber 0.3.23", + "web-time", +] + +[[package]] +name = "tracing-samply" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c175f7ecc002b6ef04776a39f440503e4e788790ddbdbfac8259b7a069526334" +dependencies = [ + "cfg-if", + "itoa", + "libc", + "mach2 0.5.0", + "memmap2", + "smallvec", + "tracing-core", + "tracing-subscriber 0.3.23", +] + +[[package]] +name = "tracing-serde" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" +dependencies = [ + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "serde", + "serde_json", + "sharded-slab", + "thread_local", + "tracing", + "tracing-core", + "tracing-serde", +] + +[[package]] +name = "tree_hash" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee44f4cef85f88b4dea21c0b1f58320bdf35715cf56d840969487cff00613321" +dependencies = [ + "alloy-primitives", + "ethereum_hashing", + "ethereum_ssz 0.9.1", + "smallvec", + "typenum", +] + +[[package]] +name = "tree_hash_derive" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bee2ea1551f90040ab0e34b6fb7f2fa3bad8acc925837ac654f2c78a13e3089" +dependencies = [ + "darling 0.20.11", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tungstenite" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.9.2", + "rustls", + "rustls-pki-types", + "sha1", + "thiserror 2.0.18", + "utf-8", +] + +[[package]] +name = "tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.9.2", + "sha1", + "thiserror 2.0.18", + "utf-8", +] + +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "uint" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "909988d098b2f738727b161a106cfc7cab00c539c2687a8836f8e565976fb53e" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-truncate" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b380a1238663e5f8a691f9039c73e1cdae598a30e9855f541d29b08b53e9a5" +dependencies = [ + "itertools 0.14.0", + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "unsigned-varint" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb066959b24b5196ae73cb057f45598450d2c5f71460e98c49b738086eff9c06" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", + "serde_derive", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37" +dependencies = [ + "getrandom 0.4.2", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "vergen" +version = "9.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b849a1f6d8639e8de261e81ee0fc881e3e3620db1af9f2e0da015d4382ceaf75" +dependencies = [ + "anyhow", + "cargo_metadata", + "derive_builder", + "regex", + "rustversion", + "time", + "vergen-lib", +] + +[[package]] +name = "vergen-git2" +version = "9.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51ab55ddf1188c8d679f349775362b0fa9e90bd7a4ac69838b2a087623f0d57" +dependencies = [ + "anyhow", + "derive_builder", + "git2", + "rustversion", + "time", + "vergen", + "vergen-lib", +] + +[[package]] +name = "vergen-lib" +version = "9.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b34a29ba7e9c59e62f229ae1932fb1b8fb8a6fdcc99215a641913f5f5a59a569" +dependencies = [ + "anyhow", + "derive_builder", + "rustversion", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" +dependencies = [ + "cfg-if", + "futures-util", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.117", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap 2.13.0", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.11.0", + "hashbrown 0.15.5", + "indexmap 2.13.0", + "semver 1.0.27", +] + +[[package]] +name = "wasmtimer" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c598d6b99ea013e35844697fc4670d08339d5cda15588f193c6beedd12f644b" +dependencies = [ + "futures", + "js-sys", + "parking_lot", + "pin-utils", + "slab", + "wasm-bindgen", +] + +[[package]] +name = "web-sys" +version = "0.3.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-root-certs" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" +dependencies = [ + "webpki-root-certs 1.0.6", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.6", +] + +[[package]] +name = "webpki-roots" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "widestring" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap 2.13.0", + "prettyplease", + "syn 2.0.117", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.117", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.11.0", + "indexmap 2.13.0", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.13.0", + "log", + "semver 1.0.27", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "ws_stream_wasm" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c173014acad22e83f16403ee360115b38846fe754e735c5d9d3803fe70c6abc" +dependencies = [ + "async_io_stream", + "futures", + "js-sys", + "log", + "pharos", + "rustc_version 0.4.1", + "send_wrapper 0.6.0", + "thiserror 2.0.18", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "xattr" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" +dependencies = [ + "libc", + "rustix", +] + +[[package]] +name = "xsum" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0637d3a5566a82fa5214bae89087bc8c9fb94cd8e8a3c07feb691bb8d9c632db" + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2578b716f8a7a858b7f02d5bd870c14bf4ddbbcf3a4c05414ba6503640505e3" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e6cc098ea4d3bd6246687de65af3f920c430e236bee1e3bf2e441463f08a02f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "serde", + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.16+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/reth-erc8004-indexer/Cargo.toml b/reth-erc8004-indexer/Cargo.toml new file mode 100644 index 00000000..75ac8fa6 --- /dev/null +++ b/reth-erc8004-indexer/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "reth-erc8004-indexer" +version = "0.1.0" +edition = "2021" +publish = false + +[[bin]] +name = "reth" +path = "src/main.rs" + +[dependencies] +alloy-primitives = { version = "1.5.6", features = ["serde"] } +alloy-sol-types = "1.3.1" +axum = { version = "0.8.6", features = ["json"] } +base64 = "0.22.1" +clap = { version = "4.5.39", features = ["derive"] } +eyre = "0.6.12" +futures = "0.3.31" +reqwest = { version = "0.12.20", default-features = false, features = ["json", "rustls-tls"] } +reth = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.1" } +reth-ethereum-cli = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.1" } +reth-execution-types = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.1" } +reth-exex = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.1", features = ["serde"] } +reth-node-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.1" } +reth-node-ethereum = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.1" } +reth-provider = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.1" } +reth-tracing = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.1" } +rusqlite = { version = "0.37.0", features = ["bundled"] } +serde = { version = "1.0.219", features = ["derive"] } +serde_json = "1.0.140" +time = { version = "0.3.41", features = ["formatting", "macros", "parsing", "serde"] } +tokio = { version = "1.47.1", features = ["full"] } + +[dev-dependencies] +http-body-util = "0.1.3" +tempfile = "3.20.0" +tower = { version = "0.5.2", features = ["util"] } diff --git a/reth-erc8004-indexer/README.md b/reth-erc8004-indexer/README.md new file mode 100644 index 00000000..c88bc1a0 --- /dev/null +++ b/reth-erc8004-indexer/README.md @@ -0,0 +1,11 @@ +# Reth ERC-8004 Indexer + +Minimal ERC-8004 discovery indexer built as a custom `reth` binary. The process: + +- runs a Reth execution node, +- installs an ExEx that indexes `Registered`, `URIUpdated`, and `MetadataSet`, +- persists state in SQLite with WAL mode, +- serves a small 8004scan-shaped REST API from the same container. + +The binary is built as `reth` so it can replace the upstream executable in the existing +`ethereum-helm-charts/reth` chart. diff --git a/reth-erc8004-indexer/src/api.rs b/reth-erc8004-indexer/src/api.rs new file mode 100644 index 00000000..38b8a0ff --- /dev/null +++ b/reth-erc8004-indexer/src/api.rs @@ -0,0 +1,390 @@ +use crate::storage::{ListOptions, Pagination, SearchOptions, Storage}; +use axum::{ + extract::{Path, Query, State}, + http::StatusCode, + response::{IntoResponse, Response}, + routing::get, + Json, Router, +}; +use serde::{Deserialize, Serialize}; +use serde_json::json; +use time::{format_description::well_known::Rfc3339, OffsetDateTime}; + +const API_VERSION: &str = "0.1.0"; + +#[derive(Debug, Clone)] +pub struct AppState { + storage: Storage, +} + +#[derive(Debug, Deserialize)] +struct AgentsQuery { + page: Option, + limit: Option, + #[serde(rename = "chainId")] + chain_id: Option, + protocol: Option, + search: Option, + #[serde(rename = "sortBy")] + sort_by: Option, + #[serde(rename = "sortOrder")] + sort_order: Option, + #[serde(rename = "ownerAddress")] + owner_address: Option, +} + +#[derive(Debug, Deserialize)] +struct SearchQuery { + #[serde(rename = "q")] + query: Option, + limit: Option, + #[serde(rename = "chainId")] + chain_id: Option, +} + +#[derive(Debug, Serialize)] +struct ResponseMeta { + version: &'static str, + timestamp: String, + #[serde(rename = "requestId")] + request_id: String, + #[serde(skip_serializing_if = "Option::is_none")] + pagination: Option, +} + +#[derive(Debug, Serialize)] +struct Envelope { + success: bool, + data: T, + meta: ResponseMeta, +} + +pub fn router(storage: Storage) -> Router { + Router::new() + .route("/health", get(health)) + .route("/api/v1/public/agents", get(list_agents)) + .route("/api/v1/public/agents/search", get(search_agents)) + .route( + "/api/v1/public/agents/{chain_id}/{token_id}", + get(get_agent), + ) + .route("/api/v1/public/stats", get(stats)) + .with_state(AppState { storage }) +} + +async fn health(State(state): State) -> Response { + match state.storage.health_snapshot() { + Ok(snapshot) => { + let status = if snapshot.ready { + StatusCode::OK + } else { + StatusCode::SERVICE_UNAVAILABLE + }; + (status, Json(snapshot)).into_response() + } + Err(error) => error_response( + StatusCode::INTERNAL_SERVER_ERROR, + "storage_error", + error.to_string(), + ), + } +} + +async fn list_agents(State(state): State, Query(query): Query) -> Response { + let options = ListOptions { + page: query.page.unwrap_or(1), + limit: query.limit.unwrap_or(20), + chain_id: query.chain_id, + protocol: query.protocol, + search: query.search, + sort_by: query.sort_by, + sort_order: query.sort_order, + owner_address: query.owner_address, + }; + + match state.storage.list_agents(&options) { + Ok(page) => success_response(page.items, Some(page.pagination)), + Err(error) => error_response( + StatusCode::INTERNAL_SERVER_ERROR, + "storage_error", + error.to_string(), + ), + } +} + +async fn get_agent( + State(state): State, + Path((chain_id, token_id)): Path<(u64, String)>, +) -> Response { + match state.storage.get_agent(chain_id, &token_id) { + Ok(Some(agent)) => success_response(agent, None), + Ok(None) => error_response(StatusCode::NOT_FOUND, "not_found", "agent not found"), + Err(error) => error_response( + StatusCode::INTERNAL_SERVER_ERROR, + "storage_error", + error.to_string(), + ), + } +} + +async fn search_agents( + State(state): State, + Query(query): Query, +) -> Response { + let Some(search_query) = query.query.map(|value| value.trim().to_owned()) else { + return error_response(StatusCode::BAD_REQUEST, "missing_query", "q is required"); + }; + if search_query.is_empty() { + return error_response(StatusCode::BAD_REQUEST, "missing_query", "q is required"); + } + + let options = SearchOptions { + query: search_query, + limit: query.limit.unwrap_or(20), + chain_id: query.chain_id, + }; + + match state.storage.search_agents(&options) { + Ok(items) => { + let pagination = Pagination { + page: 1, + limit: options.limit.clamp(1, 100), + total: items.len() as u64, + total_pages: if items.is_empty() { 0 } else { 1 }, + }; + success_response(items, Some(pagination)) + } + Err(error) => error_response( + StatusCode::INTERNAL_SERVER_ERROR, + "storage_error", + error.to_string(), + ), + } +} + +async fn stats(State(state): State) -> Response { + match state.storage.stats() { + Ok(snapshot) => success_response(snapshot, None), + Err(error) => error_response( + StatusCode::INTERNAL_SERVER_ERROR, + "storage_error", + error.to_string(), + ), + } +} + +fn success_response(data: T, pagination: Option) -> Response { + let body = Envelope { + success: true, + data, + meta: response_meta(pagination), + }; + (StatusCode::OK, Json(body)).into_response() +} + +fn error_response(status: StatusCode, code: &'static str, message: impl Into) -> Response { + let body = json!({ + "success": false, + "error": { + "code": code, + "message": message.into(), + }, + "meta": response_meta(None), + }); + (status, Json(body)).into_response() +} + +fn response_meta(pagination: Option) -> ResponseMeta { + ResponseMeta { + version: API_VERSION, + timestamp: OffsetDateTime::now_utc() + .format(&Rfc3339) + .unwrap_or_else(|_| "1970-01-01T00:00:00Z".to_owned()), + request_id: "reth-erc8004-indexer".to_owned(), + pagination, + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::storage::{BlockCursor, EventPayload, StoredEvent}; + use axum::body::Body; + use http_body_util::BodyExt; + use serde_json::Value; + use tempfile::TempDir; + use tower::ServiceExt; + + fn temp_storage() -> (TempDir, Storage) { + let dir = TempDir::new().expect("tempdir"); + let storage = Storage::new( + dir.path().join("indexer.db"), + "0x8004a818bfb912233c491871b3d84c89a494bd9e", + ); + storage.init().expect("storage init"); + (dir, storage) + } + + fn seed(storage: &Storage) { + let registration = json!({ + "name": "GPU Worker Alpha", + "description": "A search-friendly worker", + "image": "https://worker.example.com/alpha.png", + "active": true, + "x402Support": true, + "services": [ + {"name": "web", "endpoint": "https://worker.example.com/services/autoresearch-worker"}, + {"name": "OASF", "skills": ["machine_learning/model_optimization"], "domains": ["technology/artificial_intelligence/research"]} + ] + }); + storage + .apply_events( + 84532, + &BlockCursor { + block_number: 22, + block_hash: "0xblock22".to_owned(), + block_timestamp: 1_710_000_222, + }, + true, + &[StoredEvent { + chain_id: 84532, + token_id: "1".to_owned(), + block_number: 22, + block_hash: "0xblock22".to_owned(), + block_timestamp: 1_710_000_222, + tx_hash: "0xtx22".to_owned(), + tx_index: 0, + log_index: 0, + payload: EventPayload::Registered { + owner_address: "0x1111111111111111111111111111111111111111".to_owned(), + uri: "https://worker.example.com/.well-known/agent-registration.json" + .to_owned(), + registration_json: Some(registration), + fetch_error: None, + }, + }], + ) + .expect("seed storage"); + } + + async fn json_response(response: Response) -> Value { + let body = response + .into_body() + .collect() + .await + .expect("body") + .to_bytes(); + serde_json::from_slice(&body).expect("json body") + } + + #[tokio::test] + async fn health_reports_starting_before_sync() { + let (_dir, storage) = temp_storage(); + let app = router(storage); + + let response = app + .oneshot( + axum::http::Request::builder() + .uri("/health") + .body(Body::empty()) + .expect("request"), + ) + .await + .expect("response"); + + assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE); + let body = json_response(response).await; + assert_eq!(body["status"], "starting"); + } + + #[tokio::test] + async fn list_agents_returns_pagination() { + let (_dir, storage) = temp_storage(); + seed(&storage); + let app = router(storage); + + let response = app + .oneshot( + axum::http::Request::builder() + .uri("/api/v1/public/agents?page=1&limit=10&protocol=OASF&search=search-friendly") + .body(Body::empty()) + .expect("request"), + ) + .await + .expect("response"); + + assert_eq!(response.status(), StatusCode::OK); + let body = json_response(response).await; + assert_eq!(body["success"], true); + assert_eq!(body["data"].as_array().expect("items").len(), 1); + assert_eq!(body["meta"]["pagination"]["total"], 1); + } + + #[tokio::test] + async fn agent_detail_returns_raw_metadata() { + let (_dir, storage) = temp_storage(); + seed(&storage); + let app = router(storage); + + let response = app + .oneshot( + axum::http::Request::builder() + .uri("/api/v1/public/agents/84532/1") + .body(Body::empty()) + .expect("request"), + ) + .await + .expect("response"); + + assert_eq!(response.status(), StatusCode::OK); + let body = json_response(response).await; + assert_eq!( + body["data"]["raw_metadata"]["offchain_uri"], + "https://worker.example.com/.well-known/agent-registration.json" + ); + assert_eq!(body["data"]["name"], "GPU Worker Alpha"); + } + + #[tokio::test] + async fn search_agents_is_keyword_only() { + let (_dir, storage) = temp_storage(); + seed(&storage); + let app = router(storage); + + let response = app + .oneshot( + axum::http::Request::builder() + .uri("/api/v1/public/agents/search?q=model_optimization&limit=5") + .body(Body::empty()) + .expect("request"), + ) + .await + .expect("response"); + + assert_eq!(response.status(), StatusCode::OK); + let body = json_response(response).await; + assert_eq!(body["data"].as_array().expect("items").len(), 1); + } + + #[tokio::test] + async fn stats_reports_agent_totals() { + let (_dir, storage) = temp_storage(); + seed(&storage); + let app = router(storage); + + let response = app + .oneshot( + axum::http::Request::builder() + .uri("/api/v1/public/stats") + .body(Body::empty()) + .expect("request"), + ) + .await + .expect("response"); + + assert_eq!(response.status(), StatusCode::OK); + let body = json_response(response).await; + assert_eq!(body["data"]["total_agents"], 1); + assert_eq!(body["data"]["total_users"], 1); + } +} diff --git a/reth-erc8004-indexer/src/indexer.rs b/reth-erc8004-indexer/src/indexer.rs new file mode 100644 index 00000000..dc09cb7f --- /dev/null +++ b/reth-erc8004-indexer/src/indexer.rs @@ -0,0 +1,408 @@ +use crate::storage::{initial_backfill_range, BlockCursor, EventPayload, Storage, StoredEvent}; +use alloy_primitives::{keccak256, Address, B256, U256}; +use alloy_sol_types::{sol, sol_data, SolEvent, SolType}; +use base64::engine::general_purpose::STANDARD as BASE64_STANDARD; +use base64::Engine; +use eyre::{eyre, WrapErr}; +use futures::TryStreamExt; +use reth::api::{BlockBody, NodeTypes}; +use reth::chainspec::EthChainSpec; +use reth::primitives::EthPrimitives; +use reth_execution_types::Chain; +use reth_exex::{BackfillJobFactory, ExExContext}; +use reth_node_api::FullNodeComponents; +use serde_json::Value; + +type MetadataEventData = (sol_data::String, sol_data::Bytes); + +sol! { + event Registered(uint256 indexed agentId, string agentURI, address indexed owner); + event URIUpdated(uint256 indexed agentId, string newURI, address indexed updatedBy); +} + +pub async fn exex_init( + ctx: ExExContext, + storage: Storage, + http_client: reqwest::Client, + registry_address: Address, + backfill_from_block: u64, +) -> eyre::Result>> +where + Node: FullNodeComponents>, +{ + storage.init()?; + Ok(run_indexer( + ctx, + storage, + http_client, + registry_address, + backfill_from_block, + )) +} + +async fn run_indexer( + mut ctx: ExExContext, + storage: Storage, + http_client: reqwest::Client, + registry_address: Address, + backfill_from_block: u64, +) -> eyre::Result<()> +where + Node: FullNodeComponents>, +{ + let chain_id = ctx.config.chain.chain().id(); + + if let Some(range) = initial_backfill_range( + storage.current_cursor()?, + ctx.head.number, + backfill_from_block, + ) { + let job_factory = BackfillJobFactory::new(ctx.evm_config().clone(), ctx.provider().clone()); + let mut stream = job_factory.backfill(range).into_stream(); + while let Some(chain) = stream.try_next().await? { + process_committed_chain(chain_id, registry_address, &storage, &http_client, &chain) + .await?; + ctx.send_finished_height(chain.tip().num_hash())?; + } + } else if storage.current_cursor()?.is_none() { + storage.apply_events( + chain_id, + &BlockCursor { + block_number: ctx.head.number, + block_hash: ctx.head.hash.to_string(), + block_timestamp: time::OffsetDateTime::now_utc().unix_timestamp(), + }, + true, + &[], + )?; + } + + while let Some(notification) = ctx.notifications.try_next().await? { + if let Some(reverted_chain) = notification.reverted_chain() { + process_reverted_chain(chain_id, registry_address, &storage, &reverted_chain)?; + } + + if let Some(committed_chain) = notification.committed_chain() { + process_committed_chain( + chain_id, + registry_address, + &storage, + &http_client, + &committed_chain, + ) + .await?; + ctx.send_finished_height(committed_chain.tip().num_hash())?; + } + } + + Ok(()) +} + +async fn process_committed_chain( + chain_id: u64, + registry_address: Address, + storage: &Storage, + http_client: &reqwest::Client, + chain: &Chain, +) -> eyre::Result<()> { + let mut events = Vec::new(); + + for (block, receipts) in chain.blocks_and_receipts() { + for (tx_index, (tx, receipt)) in block + .body() + .transactions_iter() + .zip(receipts.iter()) + .enumerate() + { + for (log_index, log) in receipt.logs.iter().enumerate() { + if log.address != registry_address { + continue; + } + + if let Some(event) = decode_log( + chain_id, + block.num_hash().number, + block.hash(), + block.timestamp as i64, + tx.hash().to_string(), + tx_index as u32, + log_index as u32, + log.topics(), + &log.data.data, + http_client, + ) + .await? + { + events.push(event); + } + } + } + } + + storage.apply_events( + chain_id, + &BlockCursor { + block_number: chain.tip().num_hash().number, + block_hash: chain.tip().hash().to_string(), + block_timestamp: chain.tip().timestamp as i64, + }, + true, + &events, + )?; + + Ok(()) +} + +fn process_reverted_chain( + chain_id: u64, + registry_address: Address, + storage: &Storage, + chain: &Chain, +) -> eyre::Result<()> { + let mut events = Vec::new(); + for (block, receipts) in chain.blocks_and_receipts() { + for (tx_index, (tx, receipt)) in block + .body() + .transactions_iter() + .zip(receipts.iter()) + .enumerate() + { + for (log_index, log) in receipt.logs.iter().enumerate() { + if log.address != registry_address { + continue; + } + + if let Some(event) = decode_log_sync( + chain_id, + block.num_hash().number, + block.hash(), + block.timestamp as i64, + tx.hash().to_string(), + tx_index as u32, + log_index as u32, + log.topics(), + &log.data.data, + )? { + events.push(event); + } + } + } + } + + let reverted_to_block = chain.first().number.saturating_sub(1); + storage.revert_events(chain_id, reverted_to_block, &events)?; + Ok(()) +} + +async fn decode_log( + chain_id: u64, + block_number: u64, + block_hash: B256, + block_timestamp: i64, + tx_hash: String, + tx_index: u32, + log_index: u32, + topics: &[B256], + data: &[u8], + http_client: &reqwest::Client, +) -> eyre::Result> { + let Some(topic0) = topics.first().copied() else { + return Ok(None); + }; + + if topic0 == Registered::SIGNATURE_HASH { + let decoded = Registered::decode_raw_log(topics, data)?; + let (registration_json, fetch_error) = + fetch_registration_json(http_client, &decoded.agentURI).await; + return Ok(Some(StoredEvent { + chain_id, + token_id: decoded.agentId.to_string(), + block_number, + block_hash: block_hash.to_string(), + block_timestamp, + tx_hash, + tx_index, + log_index, + payload: EventPayload::Registered { + owner_address: decoded.owner.to_string(), + uri: decoded.agentURI, + registration_json, + fetch_error, + }, + })); + } + + if topic0 == URIUpdated::SIGNATURE_HASH { + let decoded = URIUpdated::decode_raw_log(topics, data)?; + let (registration_json, fetch_error) = + fetch_registration_json(http_client, &decoded.newURI).await; + return Ok(Some(StoredEvent { + chain_id, + token_id: decoded.agentId.to_string(), + block_number, + block_hash: block_hash.to_string(), + block_timestamp, + tx_hash, + tx_index, + log_index, + payload: EventPayload::UriUpdated { + updated_by: decoded.updatedBy.to_string(), + uri: decoded.newURI, + registration_json, + fetch_error, + }, + })); + } + + decode_log_sync( + chain_id, + block_number, + block_hash, + block_timestamp, + tx_hash, + tx_index, + log_index, + topics, + data, + ) +} + +fn decode_log_sync( + chain_id: u64, + block_number: u64, + block_hash: B256, + block_timestamp: i64, + tx_hash: String, + tx_index: u32, + log_index: u32, + topics: &[B256], + data: &[u8], +) -> eyre::Result> { + let Some(topic0) = topics.first().copied() else { + return Ok(None); + }; + + if topic0 != keccak256("MetadataSet(uint256,string,string,bytes)") { + return Ok(None); + } + if topics.len() < 3 { + return Err(eyre!("metadata event missing indexed topics")); + } + let (metadata_key, metadata_value): (String, alloy_primitives::Bytes) = + MetadataEventData::abi_decode_params(data) + .wrap_err("failed to decode metadata event data")?; + let metadata_value = metadata_value.to_vec(); + let metadata_text = String::from_utf8(metadata_value.clone()).ok(); + Ok(Some(StoredEvent { + chain_id, + token_id: U256::from_be_bytes(topics[1].into()).to_string(), + block_number, + block_hash: block_hash.to_string(), + block_timestamp, + tx_hash, + tx_index, + log_index, + payload: EventPayload::MetadataSet { + indexed_key_hash: topics[2].to_string(), + metadata_key, + metadata_value, + metadata_text, + }, + })) +} + +async fn fetch_registration_json( + http_client: &reqwest::Client, + uri: &str, +) -> (Option, Option) { + if uri.is_empty() { + return (None, Some("empty URI".to_owned())); + } + + if let Some(encoded) = uri.strip_prefix("data:application/json;base64,") { + match BASE64_STANDARD.decode(encoded) { + Ok(decoded) => match serde_json::from_slice::(&decoded) { + Ok(value) => return (Some(value), None), + Err(error) => return (None, Some(format!("invalid data URI JSON: {error}"))), + }, + Err(error) => return (None, Some(format!("invalid data URI base64: {error}"))), + } + } + + if !uri.starts_with("http://") && !uri.starts_with("https://") { + return (None, Some("unsupported URI scheme".to_owned())); + } + + match http_client.get(uri).send().await { + Ok(response) => match response.error_for_status() { + Ok(ok_response) => match ok_response.json::().await { + Ok(value) => (Some(value), None), + Err(error) => (None, Some(format!("invalid JSON at URI: {error}"))), + }, + Err(error) => (None, Some(format!("registration fetch failed: {error}"))), + }, + Err(error) => (None, Some(format!("registration fetch failed: {error}"))), + } +} + +pub fn build_http_client(timeout_secs: u64) -> eyre::Result { + reqwest::Client::builder() + .timeout(std::time::Duration::from_secs(timeout_secs)) + .build() + .wrap_err("failed to build reqwest client") +} + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn fetch_registration_supports_data_uris() { + let client = build_http_client(5).expect("client"); + let uri = format!( + "data:application/json;base64,{}", + BASE64_STANDARD.encode(r#"{"name":"alpha"}"#) + ); + let (registration, error) = fetch_registration_json(&client, &uri).await; + assert!(error.is_none()); + assert_eq!(registration.expect("registration")["name"], "alpha"); + } + + #[test] + fn decode_metadata_set_captures_utf8_values() { + let topics = vec![ + keccak256("MetadataSet(uint256,string,string,bytes)"), + B256::from(U256::from(7).to_be_bytes()), + B256::ZERO, + ]; + let data = MetadataEventData::abi_encode_params(&( + "metadata.best_val_bpb".to_owned(), + b"1.234".to_vec(), + )); + let decoded = decode_log_sync( + 84532, + 10, + B256::ZERO, + 1_710_000_000, + "0xtx".to_owned(), + 0, + 0, + &topics, + &data, + ) + .expect("decode") + .expect("event"); + + match decoded.payload { + EventPayload::MetadataSet { + metadata_key, + metadata_text, + .. + } => { + assert_eq!(metadata_key, "metadata.best_val_bpb"); + assert_eq!(metadata_text.as_deref(), Some("1.234")); + } + other => panic!("unexpected payload: {other:?}"), + } + } +} diff --git a/reth-erc8004-indexer/src/lib.rs b/reth-erc8004-indexer/src/lib.rs new file mode 100644 index 00000000..53071518 --- /dev/null +++ b/reth-erc8004-indexer/src/lib.rs @@ -0,0 +1,3 @@ +pub mod api; +pub mod indexer; +pub mod storage; diff --git a/reth-erc8004-indexer/src/main.rs b/reth-erc8004-indexer/src/main.rs new file mode 100644 index 00000000..fe96d772 --- /dev/null +++ b/reth-erc8004-indexer/src/main.rs @@ -0,0 +1,103 @@ +use alloy_primitives::Address; +use clap::{Args, Parser}; +use eyre::{eyre, WrapErr}; +use reth_erc8004_indexer::{api, indexer, storage::Storage}; +use reth_ethereum_cli::chainspec::EthereumChainSpecParser; +use reth_node_ethereum::EthereumNode; +use std::{net::SocketAddr, path::PathBuf}; + +#[derive(Debug, Clone, Args)] +struct IndexerArgs { + /// Address for the embedded ERC-8004 indexer HTTP server. + #[arg(long = "erc8004-indexer-http-addr", default_value = "0.0.0.0:8088")] + erc8004_indexer_http_addr: String, + + /// SQLite database path used by the embedded indexer. + #[arg( + long = "erc8004-indexer-db-path", + default_value = "/data/erc8004-indexer/indexer.db" + )] + erc8004_indexer_db_path: PathBuf, + + /// ERC-8004 identity registry contract address to index. + #[arg( + long = "erc8004-registry-address", + default_value = "0x8004A818BFB912233c491871b3d84c89A494BD9e" + )] + erc8004_registry_address: String, + + /// Historical block to begin the initial backfill from when no cursor exists. + #[arg(long = "erc8004-backfill-from-block", default_value_t = 0)] + erc8004_backfill_from_block: u64, + + /// Timeout for offchain registration fetches. + #[arg(long = "erc8004-http-timeout-seconds", default_value_t = 15)] + erc8004_http_timeout_seconds: u64, +} + +fn main() -> eyre::Result<()> { + reth::cli::Cli::::parse().run(|builder, args| async move { + let listen_addr: SocketAddr = args + .erc8004_indexer_http_addr + .parse() + .wrap_err("invalid --erc8004-indexer-http-addr")?; + let registry_address: Address = args + .erc8004_registry_address + .parse() + .wrap_err("invalid --erc8004-registry-address")?; + let storage = Storage::new( + args.erc8004_indexer_db_path.clone(), + args.erc8004_registry_address.clone(), + ); + storage.init()?; + + let http_client = indexer::build_http_client(args.erc8004_http_timeout_seconds)?; + let api_storage = storage.clone(); + let mut api_server = tokio::spawn(async move { + let listener = tokio::net::TcpListener::bind(listen_addr) + .await + .wrap_err("failed to bind ERC-8004 indexer HTTP listener")?; + axum::serve(listener, api::router(api_storage)) + .await + .wrap_err("ERC-8004 indexer HTTP server stopped unexpectedly") + }); + + let exex_storage = storage.clone(); + let exex_client = http_client.clone(); + let backfill_from_block = args.erc8004_backfill_from_block; + + let handle = builder + .node(EthereumNode::default()) + .install_exex("ERC8004Indexer", move |ctx| { + let exex_storage = exex_storage.clone(); + let exex_client = exex_client.clone(); + async move { + indexer::exex_init( + ctx, + exex_storage, + exex_client, + registry_address, + backfill_from_block, + ) + .await + } + }) + .launch_with_debug_capabilities() + .await?; + + tokio::select! { + node_exit = handle.wait_for_node_exit() => { + api_server.abort(); + node_exit + } + server_exit = &mut api_server => { + match server_exit { + Ok(Ok(())) => Err(eyre!("ERC-8004 indexer HTTP server exited unexpectedly")), + Ok(Err(error)) => Err(error.into()), + Err(error) if error.is_cancelled() => Ok(()), + Err(error) => Err(error.into()), + } + } + } + }) +} diff --git a/reth-erc8004-indexer/src/storage.rs b/reth-erc8004-indexer/src/storage.rs new file mode 100644 index 00000000..8429ecc4 --- /dev/null +++ b/reth-erc8004-indexer/src/storage.rs @@ -0,0 +1,1585 @@ +use base64::engine::general_purpose::STANDARD as BASE64_STANDARD; +use base64::Engine; +use eyre::WrapErr; +use rusqlite::{ + params, params_from_iter, + types::{Value as SqlValue, ValueRef}, + Connection, OptionalExtension, Transaction, +}; +use serde::{Deserialize, Serialize}; +use serde_json::{json, Value}; +use std::collections::{BTreeMap, BTreeSet}; +use std::path::{Path, PathBuf}; +use time::{format_description::well_known::Rfc3339, OffsetDateTime}; + +const SYNC_SCOPE: &str = "default"; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BlockCursor { + pub block_number: u64, + pub block_hash: String, + pub block_timestamp: i64, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum EventPayload { + Registered { + owner_address: String, + uri: String, + registration_json: Option, + fetch_error: Option, + }, + UriUpdated { + updated_by: String, + uri: String, + registration_json: Option, + fetch_error: Option, + }, + MetadataSet { + indexed_key_hash: String, + metadata_key: String, + metadata_value: Vec, + metadata_text: Option, + }, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct StoredEvent { + pub chain_id: u64, + pub token_id: String, + pub block_number: u64, + pub block_hash: String, + pub block_timestamp: i64, + pub tx_hash: String, + pub tx_index: u32, + pub log_index: u32, + pub payload: EventPayload, +} + +#[derive(Debug, Clone)] +pub struct ListOptions { + pub page: u64, + pub limit: u64, + pub chain_id: Option, + pub protocol: Option, + pub search: Option, + pub sort_by: Option, + pub sort_order: Option, + pub owner_address: Option, +} + +#[derive(Debug, Clone)] +pub struct SearchOptions { + pub query: String, + pub limit: u64, + pub chain_id: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct Pagination { + pub page: u64, + pub limit: u64, + pub total: u64, + #[serde(rename = "totalPages")] + pub total_pages: u64, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct AgentRecord { + pub id: String, + pub agent_id: String, + pub token_id: String, + pub chain_id: u64, + pub name: String, + pub description: String, + pub image_url: String, + pub owner_address: String, + pub uri: String, + pub active: bool, + pub x402_supported: bool, + pub supported_protocols: Vec, + pub oasf_skills: Vec, + pub oasf_domains: Vec, + pub services: Value, + pub selected_metadata: Value, + pub raw_metadata: Value, + pub registration_json: Value, + pub total_score: f64, + pub star_count: u64, + pub total_feedbacks: u64, + pub created_at: String, + pub updated_at: String, + pub last_indexed_block: u64, + pub last_indexed_at: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct AgentPage { + pub items: Vec, + pub pagination: Pagination, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct StatsSnapshot { + pub total_agents: u64, + pub total_users: u64, + pub total_feedbacks: u64, + pub total_validations: u64, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct HealthSnapshot { + pub status: String, + pub ready: bool, + pub chain_id: Option, + pub registry_address: String, + pub latest_indexed_block: Option, + pub latest_indexed_hash: Option, + pub latest_indexed_at: Option, + pub backfilled: bool, + pub last_error: Option, +} + +#[derive(Debug, Clone)] +pub struct Storage { + path: PathBuf, + registry_address: String, +} + +#[derive(Debug, Default)] +struct DerivedAgent { + owner_address: String, + uri: String, + registration_json: Option, + fetch_error: Option, + metadata: BTreeMap, + created_at: i64, + updated_at: i64, + last_indexed_block: u64, + last_indexed_at: i64, +} + +#[derive(Debug, Default)] +struct ParsedRegistration { + name: String, + description: String, + image_url: String, + active: bool, + x402_supported: bool, + supported_protocols: Vec, + services: Value, + oasf_skills: Vec, + oasf_domains: Vec, +} + +impl Storage { + pub fn new(path: impl Into, registry_address: impl Into) -> Self { + Self { + path: path.into(), + registry_address: registry_address.into().to_lowercase(), + } + } + + pub fn path(&self) -> &Path { + &self.path + } + + pub fn init(&self) -> eyre::Result<()> { + if let Some(parent) = self.path.parent() { + std::fs::create_dir_all(parent) + .wrap_err_with(|| format!("failed to create {}", parent.display()))?; + } + + let conn = self.open_connection()?; + conn.execute_batch( + " + CREATE TABLE IF NOT EXISTS agent_events ( + id TEXT PRIMARY KEY, + chain_id INTEGER NOT NULL, + token_id TEXT NOT NULL, + block_number INTEGER NOT NULL, + block_hash TEXT NOT NULL, + block_timestamp INTEGER NOT NULL, + tx_hash TEXT NOT NULL, + tx_index INTEGER NOT NULL, + log_index INTEGER NOT NULL, + event_kind TEXT NOT NULL, + owner_address TEXT, + actor_address TEXT, + uri TEXT, + registration_json TEXT, + fetch_error TEXT, + indexed_key_hash TEXT, + metadata_key TEXT, + metadata_value BLOB, + metadata_text TEXT + ); + CREATE INDEX IF NOT EXISTS idx_agent_events_chain_token + ON agent_events (chain_id, token_id, block_number, tx_index, log_index); + CREATE INDEX IF NOT EXISTS idx_agent_events_chain_block + ON agent_events (chain_id, block_number); + + CREATE TABLE IF NOT EXISTS agents ( + chain_id INTEGER NOT NULL, + token_id TEXT NOT NULL, + agent_id TEXT NOT NULL, + owner_address TEXT NOT NULL DEFAULT '', + uri TEXT NOT NULL DEFAULT '', + name TEXT NOT NULL DEFAULT '', + description TEXT NOT NULL DEFAULT '', + image_url TEXT NOT NULL DEFAULT '', + active INTEGER NOT NULL DEFAULT 0, + x402_supported INTEGER NOT NULL DEFAULT 0, + supported_protocols_json TEXT NOT NULL DEFAULT '[]', + services_json TEXT NOT NULL DEFAULT '[]', + oasf_skills_json TEXT NOT NULL DEFAULT '[]', + oasf_domains_json TEXT NOT NULL DEFAULT '[]', + selected_metadata_json TEXT NOT NULL DEFAULT '{}', + raw_metadata_json TEXT NOT NULL DEFAULT '{}', + registration_json TEXT, + fetch_error TEXT, + search_text TEXT NOT NULL DEFAULT '', + created_at INTEGER NOT NULL DEFAULT 0, + updated_at INTEGER NOT NULL DEFAULT 0, + last_indexed_block INTEGER NOT NULL DEFAULT 0, + last_indexed_at INTEGER NOT NULL DEFAULT 0, + total_score REAL NOT NULL DEFAULT 0, + star_count INTEGER NOT NULL DEFAULT 0, + total_feedbacks INTEGER NOT NULL DEFAULT 0, + PRIMARY KEY (chain_id, token_id) + ); + + CREATE TABLE IF NOT EXISTS sync_state ( + scope TEXT PRIMARY KEY, + chain_id INTEGER, + registry_address TEXT NOT NULL, + ready INTEGER NOT NULL DEFAULT 0, + backfilled INTEGER NOT NULL DEFAULT 0, + latest_processed_block INTEGER, + latest_processed_hash TEXT, + latest_processed_at INTEGER, + last_error TEXT + ); + ", + )?; + conn.execute( + " + INSERT INTO sync_state ( + scope, + registry_address, + ready, + backfilled + ) VALUES (?1, ?2, 0, 0) + ON CONFLICT(scope) DO NOTHING + ", + params![SYNC_SCOPE, self.registry_address], + )?; + + Ok(()) + } + + pub fn current_cursor(&self) -> eyre::Result> { + let conn = self.open_connection()?; + let cursor: Option = conn.query_row( + "SELECT latest_processed_block FROM sync_state WHERE scope = ?1", + params![SYNC_SCOPE], + |row| row.get(0), + )?; + Ok(cursor.and_then(|value| u64::try_from(value).ok())) + } + + pub fn apply_events( + &self, + chain_id: u64, + cursor: &BlockCursor, + mark_backfilled: bool, + events: &[StoredEvent], + ) -> eyre::Result<()> { + let mut conn = self.open_connection()?; + let tx = conn.transaction()?; + + let mut affected = BTreeSet::new(); + for event in events { + affected.insert(event.token_id.clone()); + self.insert_event(&tx, event)?; + } + + for token_id in affected { + self.rebuild_agent(&tx, chain_id, &token_id)?; + } + + tx.execute( + " + UPDATE sync_state + SET chain_id = ?2, + ready = 1, + backfilled = CASE WHEN ?3 THEN 1 ELSE backfilled END, + latest_processed_block = ?4, + latest_processed_hash = ?5, + latest_processed_at = ?6, + last_error = NULL + WHERE scope = ?1 + ", + params![ + SYNC_SCOPE, + chain_id as i64, + mark_backfilled, + cursor.block_number as i64, + cursor.block_hash.as_str(), + cursor.block_timestamp, + ], + )?; + tx.commit()?; + Ok(()) + } + + pub fn revert_events( + &self, + chain_id: u64, + reverted_to_block: u64, + events: &[StoredEvent], + ) -> eyre::Result<()> { + let mut conn = self.open_connection()?; + let tx = conn.transaction()?; + let mut affected = BTreeSet::new(); + + for event in events { + affected.insert(event.token_id.clone()); + tx.execute( + "DELETE FROM agent_events WHERE id = ?1", + params![event.event_id()], + )?; + } + + for token_id in affected { + self.rebuild_agent(&tx, chain_id, &token_id)?; + } + + tx.execute( + " + UPDATE sync_state + SET chain_id = ?2, + ready = 1, + latest_processed_block = ?3, + latest_processed_hash = NULL, + latest_processed_at = ?4, + last_error = NULL + WHERE scope = ?1 + ", + params![ + SYNC_SCOPE, + chain_id as i64, + reverted_to_block as i64, + now_utc(), + ], + )?; + tx.commit()?; + Ok(()) + } + + pub fn mark_unhealthy( + &self, + chain_id: Option, + message: impl Into, + ) -> eyre::Result<()> { + let conn = self.open_connection()?; + conn.execute( + " + UPDATE sync_state + SET chain_id = COALESCE(?2, chain_id), + ready = 0, + last_error = ?3, + latest_processed_at = ?4 + WHERE scope = ?1 + ", + params![ + SYNC_SCOPE, + chain_id.map(|value| value as i64), + message.into(), + now_utc() + ], + )?; + Ok(()) + } + + pub fn health_snapshot(&self) -> eyre::Result { + let conn = self.open_connection()?; + let snapshot = conn.query_row( + " + SELECT + chain_id, + registry_address, + ready, + backfilled, + latest_processed_block, + latest_processed_hash, + latest_processed_at, + last_error + FROM sync_state + WHERE scope = ?1 + ", + params![SYNC_SCOPE], + |row| { + let chain_id: Option = row.get(0)?; + let registry_address: String = row.get(1)?; + let ready: bool = row.get(2)?; + let backfilled: bool = row.get(3)?; + let latest_processed_block: Option = row.get(4)?; + let latest_processed_hash: Option = row.get(5)?; + let latest_processed_at: Option = row.get(6)?; + let last_error: Option = row.get(7)?; + + let status = if ready { + "ok" + } else if last_error.is_some() { + "unhealthy" + } else { + "starting" + }; + + Ok(HealthSnapshot { + status: status.to_owned(), + ready, + chain_id: chain_id.and_then(|value| u64::try_from(value).ok()), + registry_address, + latest_indexed_block: latest_processed_block + .and_then(|value| u64::try_from(value).ok()), + latest_indexed_hash: latest_processed_hash, + latest_indexed_at: latest_processed_at.map(format_timestamp), + backfilled, + last_error, + }) + }, + )?; + Ok(snapshot) + } + + pub fn get_agent(&self, chain_id: u64, token_id: &str) -> eyre::Result> { + let conn = self.open_connection()?; + self.get_agent_with_conn(&conn, chain_id, token_id) + } + + pub fn list_agents(&self, options: &ListOptions) -> eyre::Result { + let conn = self.open_connection()?; + let page = options.page.max(1); + let limit = options.limit.clamp(1, 100); + let offset = ((page - 1) * limit) as i64; + + let (where_sql, query_params) = build_where_clause( + options.chain_id, + options.protocol.as_deref(), + options.search.as_deref(), + options.owner_address.as_deref(), + ); + let count_sql = format!("SELECT COUNT(*) FROM agents WHERE {where_sql}"); + let total: i64 = + conn.query_row(&count_sql, params_from_iter(query_params.iter()), |row| { + row.get(0) + })?; + + let sort_by = sort_expression(options.sort_by.as_deref()); + let sort_order = sort_order(options.sort_order.as_deref()); + let list_sql = format!( + " + SELECT + chain_id, + token_id, + agent_id, + owner_address, + uri, + name, + description, + image_url, + active, + x402_supported, + supported_protocols_json, + services_json, + oasf_skills_json, + oasf_domains_json, + selected_metadata_json, + raw_metadata_json, + registration_json, + total_score, + star_count, + total_feedbacks, + created_at, + updated_at, + last_indexed_block, + last_indexed_at + FROM agents + WHERE {where_sql} + ORDER BY {sort_by} {sort_order} + LIMIT ? OFFSET ? + " + ); + + let mut params_with_window = query_params.clone(); + params_with_window.push(SqlValue::Integer(limit as i64)); + params_with_window.push(SqlValue::Integer(offset)); + + let mut stmt = conn.prepare(&list_sql)?; + let rows = stmt.query_map(params_from_iter(params_with_window.iter()), |row| { + map_agent_row(row) + })?; + let items = rows.collect::, _>>()?; + + let total = total.max(0) as u64; + let total_pages = if total == 0 { 0 } else { total.div_ceil(limit) }; + Ok(AgentPage { + items, + pagination: Pagination { + page, + limit, + total, + total_pages, + }, + }) + } + + pub fn search_agents(&self, options: &SearchOptions) -> eyre::Result> { + let conn = self.open_connection()?; + let query = options.query.trim().to_lowercase(); + if query.is_empty() { + return Ok(Vec::new()); + } + + let mut sql = " + SELECT + chain_id, + token_id, + agent_id, + owner_address, + uri, + name, + description, + image_url, + active, + x402_supported, + supported_protocols_json, + services_json, + oasf_skills_json, + oasf_domains_json, + selected_metadata_json, + raw_metadata_json, + registration_json, + total_score, + star_count, + total_feedbacks, + created_at, + updated_at, + last_indexed_block, + last_indexed_at + FROM agents + WHERE search_text LIKE ? + " + .to_owned(); + let mut params = vec![SqlValue::Text(format!("%{query}%"))]; + + if let Some(chain_id) = options.chain_id { + sql.push_str(" AND chain_id = ?"); + params.push(SqlValue::Integer(chain_id as i64)); + } + + sql.push_str(" ORDER BY updated_at DESC, name ASC LIMIT ?"); + params.push(SqlValue::Integer(options.limit.clamp(1, 100) as i64)); + + let mut stmt = conn.prepare(&sql)?; + let rows = stmt.query_map(params_from_iter(params.iter()), |row| map_agent_row(row))?; + let items = rows.collect::, _>>()?; + Ok(items) + } + + pub fn stats(&self) -> eyre::Result { + let conn = self.open_connection()?; + let total_agents: i64 = + conn.query_row("SELECT COUNT(*) FROM agents", [], |row| row.get(0))?; + let total_users: i64 = conn.query_row( + "SELECT COUNT(DISTINCT owner_address) FROM agents WHERE owner_address <> ''", + [], + |row| row.get(0), + )?; + Ok(StatsSnapshot { + total_agents: total_agents.max(0) as u64, + total_users: total_users.max(0) as u64, + total_feedbacks: 0, + total_validations: 0, + }) + } + + fn open_connection(&self) -> eyre::Result { + let conn = Connection::open(&self.path) + .wrap_err_with(|| format!("failed to open {}", self.path.display()))?; + conn.pragma_update(None, "journal_mode", "WAL")?; + conn.pragma_update(None, "synchronous", "NORMAL")?; + conn.pragma_update(None, "foreign_keys", "ON")?; + conn.busy_timeout(std::time::Duration::from_secs(5))?; + Ok(conn) + } + + fn insert_event(&self, tx: &Transaction<'_>, event: &StoredEvent) -> eyre::Result<()> { + match &event.payload { + EventPayload::Registered { + owner_address, + uri, + registration_json, + fetch_error, + } => { + tx.execute( + " + INSERT OR REPLACE INTO agent_events ( + id, chain_id, token_id, block_number, block_hash, block_timestamp, + tx_hash, tx_index, log_index, event_kind, owner_address, uri, + registration_json, fetch_error + ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, 'registered', ?10, ?11, ?12, ?13) + ", + params![ + event.event_id(), + event.chain_id as i64, + event.token_id.as_str(), + event.block_number as i64, + event.block_hash.as_str(), + event.block_timestamp, + event.tx_hash.as_str(), + event.tx_index as i64, + event.log_index as i64, + owner_address, + uri, + json_to_string(registration_json.as_ref()), + fetch_error, + ], + )?; + } + EventPayload::UriUpdated { + updated_by, + uri, + registration_json, + fetch_error, + } => { + tx.execute( + " + INSERT OR REPLACE INTO agent_events ( + id, chain_id, token_id, block_number, block_hash, block_timestamp, + tx_hash, tx_index, log_index, event_kind, actor_address, uri, + registration_json, fetch_error + ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, 'uri_updated', ?10, ?11, ?12, ?13) + ", + params![ + event.event_id(), + event.chain_id as i64, + event.token_id.as_str(), + event.block_number as i64, + event.block_hash.as_str(), + event.block_timestamp, + event.tx_hash.as_str(), + event.tx_index as i64, + event.log_index as i64, + updated_by, + uri, + json_to_string(registration_json.as_ref()), + fetch_error, + ], + )?; + } + EventPayload::MetadataSet { + indexed_key_hash, + metadata_key, + metadata_value, + metadata_text, + } => { + tx.execute( + " + INSERT OR REPLACE INTO agent_events ( + id, chain_id, token_id, block_number, block_hash, block_timestamp, + tx_hash, tx_index, log_index, event_kind, indexed_key_hash, + metadata_key, metadata_value, metadata_text + ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, 'metadata_set', ?10, ?11, ?12, ?13) + ", + params![ + event.event_id(), + event.chain_id as i64, + event.token_id.as_str(), + event.block_number as i64, + event.block_hash.as_str(), + event.block_timestamp, + event.tx_hash.as_str(), + event.tx_index as i64, + event.log_index as i64, + indexed_key_hash, + metadata_key, + metadata_value, + metadata_text, + ], + )?; + } + } + Ok(()) + } + + fn rebuild_agent( + &self, + tx: &Transaction<'_>, + chain_id: u64, + token_id: &str, + ) -> eyre::Result<()> { + let mut stmt = tx.prepare( + " + SELECT + event_kind, + owner_address, + uri, + registration_json, + fetch_error, + metadata_key, + metadata_value, + metadata_text, + block_timestamp, + block_number + FROM agent_events + WHERE chain_id = ?1 AND token_id = ?2 + ORDER BY block_number ASC, tx_index ASC, log_index ASC + ", + )?; + + let rows = stmt.query_map(params![chain_id as i64, token_id], |row| { + Ok(( + row.get::<_, String>(0)?, + row.get::<_, Option>(1)?, + row.get::<_, Option>(2)?, + row.get::<_, Option>(3)?, + row.get::<_, Option>(4)?, + row.get::<_, Option>(5)?, + read_blob(row.get_ref(6)?), + row.get::<_, Option>(7)?, + row.get::<_, i64>(8)?, + row.get::<_, i64>(9)?, + )) + })?; + + let mut derived = DerivedAgent::default(); + let mut found = false; + + for row in rows { + let ( + event_kind, + owner_address, + uri, + registration_json, + fetch_error, + metadata_key, + metadata_value, + metadata_text, + block_timestamp, + block_number, + ) = row?; + + found = true; + if derived.created_at == 0 { + derived.created_at = block_timestamp; + } + derived.updated_at = block_timestamp; + derived.last_indexed_at = block_timestamp; + derived.last_indexed_block = block_number.max(0) as u64; + + match event_kind.as_str() { + "registered" => { + if let Some(value) = owner_address { + derived.owner_address = value.to_lowercase(); + } + if let Some(value) = uri { + derived.uri = value; + } + derived.registration_json = parse_optional_json(registration_json)?; + derived.fetch_error = fetch_error; + } + "uri_updated" => { + if let Some(value) = uri { + derived.uri = value; + } + derived.registration_json = parse_optional_json(registration_json)?; + derived.fetch_error = fetch_error; + } + "metadata_set" => { + if let Some(key) = metadata_key { + derived + .metadata + .insert(key, metadata_json_value(metadata_text, &metadata_value)); + } + } + _ => {} + } + } + + if !found { + tx.execute( + "DELETE FROM agents WHERE chain_id = ?1 AND token_id = ?2", + params![chain_id as i64, token_id], + )?; + return Ok(()); + } + + let parsed = parse_registration(derived.registration_json.as_ref(), &derived.metadata); + let raw_metadata = json!({ + "offchain_uri": derived.uri, + "offchain_content": derived.registration_json, + "onchain_indexed": derived.metadata, + "fetch_error": derived.fetch_error, + }); + let search_text = build_search_text(&parsed, &derived.metadata).to_lowercase(); + + tx.execute( + " + INSERT OR REPLACE INTO agents ( + chain_id, + token_id, + agent_id, + owner_address, + uri, + name, + description, + image_url, + active, + x402_supported, + supported_protocols_json, + services_json, + oasf_skills_json, + oasf_domains_json, + selected_metadata_json, + raw_metadata_json, + registration_json, + fetch_error, + search_text, + created_at, + updated_at, + last_indexed_block, + last_indexed_at + ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20, ?21, ?22, ?23) + ", + params![ + chain_id as i64, + token_id, + format!("{}:{}:{}", chain_id, self.registry_address, token_id), + derived.owner_address, + derived.uri, + parsed.name, + parsed.description, + parsed.image_url, + parsed.active, + parsed.x402_supported, + serde_json::to_string(&parsed.supported_protocols)?, + serde_json::to_string(&parsed.services)?, + serde_json::to_string(&parsed.oasf_skills)?, + serde_json::to_string(&parsed.oasf_domains)?, + serde_json::to_string(&derived.metadata)?, + serde_json::to_string(&raw_metadata)?, + json_to_string(derived.registration_json.as_ref()), + derived.fetch_error, + search_text, + derived.created_at, + derived.updated_at, + derived.last_indexed_block as i64, + derived.last_indexed_at, + ], + )?; + + Ok(()) + } + + fn get_agent_with_conn( + &self, + conn: &Connection, + chain_id: u64, + token_id: &str, + ) -> eyre::Result> { + let mut stmt = conn.prepare( + " + SELECT + chain_id, + token_id, + agent_id, + owner_address, + uri, + name, + description, + image_url, + active, + x402_supported, + supported_protocols_json, + services_json, + oasf_skills_json, + oasf_domains_json, + selected_metadata_json, + raw_metadata_json, + registration_json, + total_score, + star_count, + total_feedbacks, + created_at, + updated_at, + last_indexed_block, + last_indexed_at + FROM agents + WHERE chain_id = ?1 AND token_id = ?2 + ", + )?; + + let agent = stmt + .query_row(params![chain_id as i64, token_id], |row| map_agent_row(row)) + .optional()?; + Ok(agent) + } +} + +impl StoredEvent { + pub fn event_id(&self) -> String { + format!( + "{}:{}:{}", + self.block_hash.to_lowercase(), + self.tx_hash.to_lowercase(), + self.log_index + ) + } +} + +pub fn initial_backfill_range( + cursor: Option, + current_head: u64, + from_block: u64, +) -> Option> { + let start = cursor.map_or(from_block, |value| value.saturating_add(1)); + (start <= current_head).then_some(start..=current_head) +} + +fn build_where_clause( + chain_id: Option, + protocol: Option<&str>, + search: Option<&str>, + owner_address: Option<&str>, +) -> (String, Vec) { + let mut clauses = vec!["1 = 1".to_owned()]; + let mut params = Vec::new(); + + if let Some(chain_id) = chain_id { + clauses.push("chain_id = ?".to_owned()); + params.push(SqlValue::Integer(chain_id as i64)); + } + + if let Some(protocol) = protocol { + clauses.push("lower(supported_protocols_json) LIKE ?".to_owned()); + params.push(SqlValue::Text(format!( + "%{}%", + protocol.trim().to_lowercase() + ))); + } + + if let Some(search) = search { + clauses.push("search_text LIKE ?".to_owned()); + params.push(SqlValue::Text(format!( + "%{}%", + search.trim().to_lowercase() + ))); + } + + if let Some(owner_address) = owner_address { + clauses.push("owner_address = ?".to_owned()); + params.push(SqlValue::Text(owner_address.trim().to_lowercase())); + } + + (clauses.join(" AND "), params) +} + +fn sort_expression(sort_by: Option<&str>) -> &'static str { + match sort_by.unwrap_or("created_at") { + "name" => "name COLLATE NOCASE", + "token_id" => "CAST(token_id AS INTEGER)", + "total_score" => "total_score", + "stars" => "star_count", + _ => "created_at", + } +} + +fn sort_order(sort_order: Option<&str>) -> &'static str { + match sort_order.unwrap_or("desc").to_lowercase().as_str() { + "asc" => "ASC", + _ => "DESC", + } +} + +fn map_agent_row(row: &rusqlite::Row<'_>) -> rusqlite::Result { + let chain_id: i64 = row.get(0)?; + let token_id: String = row.get(1)?; + let active: bool = row.get(8)?; + let x402_supported: bool = row.get(9)?; + let total_score: f64 = row.get(17)?; + let star_count: i64 = row.get(18)?; + let total_feedbacks: i64 = row.get(19)?; + let created_at: i64 = row.get(20)?; + let updated_at: i64 = row.get(21)?; + let last_indexed_block: i64 = row.get(22)?; + let last_indexed_at: i64 = row.get(23)?; + + Ok(AgentRecord { + id: row.get(2)?, + agent_id: row.get(2)?, + token_id, + chain_id: chain_id.max(0) as u64, + owner_address: row.get::<_, String>(3)?, + uri: row.get::<_, String>(4)?, + name: row.get::<_, String>(5)?, + description: row.get::<_, String>(6)?, + image_url: row.get::<_, String>(7)?, + active, + x402_supported, + supported_protocols: parse_json_string_array(row.get::<_, String>(10)?)?, + services: parse_json_value(row.get::<_, String>(11)?)?, + oasf_skills: parse_json_string_array(row.get::<_, String>(12)?)?, + oasf_domains: parse_json_string_array(row.get::<_, String>(13)?)?, + selected_metadata: parse_json_value(row.get::<_, String>(14)?)?, + raw_metadata: parse_json_value(row.get::<_, String>(15)?)?, + registration_json: parse_optional_json(row.get::<_, Option>(16)?) + .map_err(|error| { + rusqlite::Error::FromSqlConversionFailure( + 16, + rusqlite::types::Type::Text, + Box::new(error), + ) + })? + .unwrap_or(Value::Null), + total_score, + star_count: star_count.max(0) as u64, + total_feedbacks: total_feedbacks.max(0) as u64, + created_at: format_timestamp(created_at), + updated_at: format_timestamp(updated_at), + last_indexed_block: last_indexed_block.max(0) as u64, + last_indexed_at: format_timestamp(last_indexed_at), + }) +} + +fn parse_registration( + registration_json: Option<&Value>, + metadata: &BTreeMap, +) -> ParsedRegistration { + let mut parsed = ParsedRegistration { + active: true, + services: Value::Array(Vec::new()), + ..Default::default() + }; + + if let Some(Value::Object(registration)) = registration_json { + parsed.name = string_field(registration.get("name")); + parsed.description = string_field(registration.get("description")); + parsed.image_url = string_field(registration.get("image")); + parsed.active = registration + .get("active") + .and_then(Value::as_bool) + .unwrap_or(true); + parsed.x402_supported = registration + .get("x402Support") + .and_then(Value::as_bool) + .unwrap_or(false); + + if let Some(services) = registration.get("services") { + parsed.services = services.clone(); + if let Some(array) = services.as_array() { + let mut protocols = BTreeSet::new(); + let mut skills = BTreeSet::new(); + let mut domains = BTreeSet::new(); + for entry in array { + let Some(service) = entry.as_object() else { + continue; + }; + if let Some(name) = service.get("name").and_then(Value::as_str) { + protocols.insert(name.to_owned()); + if name.eq_ignore_ascii_case("OASF") { + collect_string_list(service.get("skills"), &mut skills); + collect_string_list(service.get("domains"), &mut domains); + } + } + } + parsed.supported_protocols = protocols.into_iter().collect(); + parsed.oasf_skills = skills.into_iter().collect(); + parsed.oasf_domains = domains.into_iter().collect(); + } + } + } + + if let Some(service_type) = metadata.get("service.type").and_then(Value::as_str) { + if !parsed + .supported_protocols + .iter() + .any(|item| item.eq_ignore_ascii_case(service_type)) + { + parsed.supported_protocols.push(service_type.to_owned()); + } + } + + if metadata.get("x402.supported").is_some() { + parsed.x402_supported = metadata_flag(metadata.get("x402.supported")); + } + + parsed +} + +fn build_search_text(parsed: &ParsedRegistration, metadata: &BTreeMap) -> String { + let mut parts = vec![ + parsed.name.clone(), + parsed.description.clone(), + parsed.image_url.clone(), + ]; + parts.extend(parsed.supported_protocols.clone()); + parts.extend(parsed.oasf_skills.clone()); + parts.extend(parsed.oasf_domains.clone()); + for value in metadata.values() { + match value { + Value::String(text) => parts.push(text.clone()), + other => parts.push(other.to_string()), + } + } + parts.join(" ") +} + +fn collect_string_list(value: Option<&Value>, target: &mut BTreeSet) { + let Some(Value::Array(values)) = value else { + return; + }; + for entry in values { + if let Some(text) = entry.as_str() { + if !text.trim().is_empty() { + target.insert(text.to_owned()); + } + } + } +} + +fn metadata_flag(value: Option<&Value>) -> bool { + match value { + Some(Value::Bool(flag)) => *flag, + Some(Value::Number(number)) => number.as_u64().unwrap_or(0) > 0, + Some(Value::String(text)) => { + let normalized = text.trim().to_lowercase(); + normalized == "1" || normalized == "true" || normalized == "yes" + } + Some(Value::Object(object)) => object + .get("base64") + .and_then(Value::as_str) + .and_then(|encoded| BASE64_STANDARD.decode(encoded).ok()) + .is_some_and(|bytes| !bytes.is_empty() && bytes.iter().any(|byte| *byte != 0)), + _ => false, + } +} + +fn metadata_json_value(metadata_text: Option, metadata_value: &[u8]) -> Value { + if let Some(text) = metadata_text { + if text == "\u{1}" || text == "1" { + return Value::Bool(true); + } + if text.eq_ignore_ascii_case("true") { + return Value::Bool(true); + } + return Value::String(text); + } + + if metadata_value == [1] { + return Value::Bool(true); + } + + json!({ "base64": BASE64_STANDARD.encode(metadata_value) }) +} + +fn read_blob(value: ValueRef<'_>) -> Vec { + match value { + ValueRef::Blob(bytes) => bytes.to_vec(), + ValueRef::Text(text) => text.to_vec(), + _ => Vec::new(), + } +} + +fn json_to_string(value: Option<&Value>) -> Option { + value.and_then(|item| serde_json::to_string(item).ok()) +} + +fn parse_optional_json(value: Option) -> serde_json::Result> { + match value { + Some(raw) => Ok(Some(serde_json::from_str(&raw)?)), + None => Ok(None), + } +} + +fn parse_json_value(value: String) -> rusqlite::Result { + serde_json::from_str(&value).map_err(|error| { + rusqlite::Error::FromSqlConversionFailure(0, rusqlite::types::Type::Text, Box::new(error)) + }) +} + +fn parse_json_string_array(value: String) -> rusqlite::Result> { + serde_json::from_str(&value).map_err(|error| { + rusqlite::Error::FromSqlConversionFailure(0, rusqlite::types::Type::Text, Box::new(error)) + }) +} + +fn string_field(value: Option<&Value>) -> String { + value.and_then(Value::as_str).unwrap_or_default().to_owned() +} + +fn format_timestamp(timestamp: i64) -> String { + OffsetDateTime::from_unix_timestamp(timestamp) + .unwrap_or(OffsetDateTime::UNIX_EPOCH) + .format(&Rfc3339) + .unwrap_or_else(|_| "1970-01-01T00:00:00Z".to_owned()) +} + +fn now_utc() -> i64 { + OffsetDateTime::now_utc().unix_timestamp() +} + +#[cfg(test)] +mod tests { + use super::*; + use tempfile::TempDir; + + fn temp_storage() -> (TempDir, Storage) { + let dir = TempDir::new().expect("tempdir"); + let storage = Storage::new( + dir.path().join("indexer.db"), + "0x8004a818bfb912233c491871b3d84c89a494bd9e", + ); + storage.init().expect("storage init"); + (dir, storage) + } + + fn registration(name: &str, endpoint: &str) -> Value { + json!({ + "type": "https://eips.ethereum.org/EIPS/eip-8004#registration-v1", + "name": name, + "description": format!("{name} description"), + "image": format!("https://example.com/{name}.png"), + "active": true, + "x402Support": true, + "services": [ + {"name": "web", "endpoint": endpoint, "version": "1.0.0"}, + {"name": "OASF", "version": "0.8", "skills": ["machine_learning/model_optimization"], "domains": ["technology/artificial_intelligence/research"]} + ] + }) + } + + fn registered_event(token_id: &str, uri: &str, ts: i64, registration: Value) -> StoredEvent { + StoredEvent { + chain_id: 84532, + token_id: token_id.to_owned(), + block_number: 10, + block_hash: "0xblock10".to_owned(), + block_timestamp: ts, + tx_hash: "0xtx10".to_owned(), + tx_index: 0, + log_index: 0, + payload: EventPayload::Registered { + owner_address: "0x1111111111111111111111111111111111111111".to_owned(), + uri: uri.to_owned(), + registration_json: Some(registration), + fetch_error: None, + }, + } + } + + fn uri_updated_event(token_id: &str, uri: &str, ts: i64, registration: Value) -> StoredEvent { + StoredEvent { + chain_id: 84532, + token_id: token_id.to_owned(), + block_number: 11, + block_hash: "0xblock11".to_owned(), + block_timestamp: ts, + tx_hash: "0xtx11".to_owned(), + tx_index: 0, + log_index: 1, + payload: EventPayload::UriUpdated { + updated_by: "0x2222222222222222222222222222222222222222".to_owned(), + uri: uri.to_owned(), + registration_json: Some(registration), + fetch_error: None, + }, + } + } + + fn metadata_event(token_id: &str, key: &str, value: &[u8], text: Option<&str>) -> StoredEvent { + StoredEvent { + chain_id: 84532, + token_id: token_id.to_owned(), + block_number: 12, + block_hash: "0xblock12".to_owned(), + block_timestamp: 1_710_000_222, + tx_hash: "0xtx12".to_owned(), + tx_index: 0, + log_index: 2, + payload: EventPayload::MetadataSet { + indexed_key_hash: "0xhash".to_owned(), + metadata_key: key.to_owned(), + metadata_value: value.to_vec(), + metadata_text: text.map(ToOwned::to_owned), + }, + } + } + + #[test] + fn apply_commit_builds_agent_summary() { + let (_dir, storage) = temp_storage(); + let event = registered_event( + "1", + "https://worker.example.com/.well-known/agent-registration.json", + 1_710_000_000, + registration( + "GPU Worker Alpha", + "https://worker.example.com/services/autoresearch-worker", + ), + ); + storage + .apply_events( + 84532, + &BlockCursor { + block_number: 10, + block_hash: "0xblock10".to_owned(), + block_timestamp: 1_710_000_000, + }, + true, + &[event], + ) + .expect("apply events"); + + let agent = storage + .get_agent(84532, "1") + .expect("get agent") + .expect("agent"); + assert_eq!(agent.name, "GPU Worker Alpha"); + assert!(agent.x402_supported); + assert_eq!( + agent.supported_protocols, + vec!["OASF".to_owned(), "web".to_owned()] + ); + assert_eq!( + agent.oasf_skills, + vec!["machine_learning/model_optimization".to_owned()] + ); + } + + #[test] + fn reorg_rollback_restores_previous_uri() { + let (_dir, storage) = temp_storage(); + let base = registered_event( + "1", + "https://worker.example.com/.well-known/agent-registration.json", + 1_710_000_000, + registration( + "GPU Worker Alpha", + "https://worker.example.com/services/autoresearch-worker", + ), + ); + let updated = uri_updated_event( + "1", + "https://worker.example.com/v2/agent-registration.json", + 1_710_000_111, + registration( + "GPU Worker Beta", + "https://worker.example.com/services/autoresearch-worker-v2", + ), + ); + + storage + .apply_events( + 84532, + &BlockCursor { + block_number: 11, + block_hash: "0xblock11".to_owned(), + block_timestamp: 1_710_000_111, + }, + true, + &[base.clone(), updated.clone()], + ) + .expect("apply events"); + assert_eq!( + storage + .get_agent(84532, "1") + .expect("get agent") + .expect("agent") + .name, + "GPU Worker Beta" + ); + + storage + .revert_events(84532, 10, &[updated]) + .expect("revert"); + let agent = storage + .get_agent(84532, "1") + .expect("get agent") + .expect("agent"); + assert_eq!(agent.name, "GPU Worker Alpha"); + assert_eq!( + agent.uri, + "https://worker.example.com/.well-known/agent-registration.json" + ); + } + + #[test] + fn metadata_updates_are_projected_into_agent_state() { + let (_dir, storage) = temp_storage(); + let base = registered_event( + "1", + "https://worker.example.com/.well-known/agent-registration.json", + 1_710_000_000, + registration( + "GPU Worker Alpha", + "https://worker.example.com/services/autoresearch-worker", + ), + ); + let metadata = metadata_event("1", "metadata.best_val_bpb", b"1.111", Some("1.111")); + + storage + .apply_events( + 84532, + &BlockCursor { + block_number: 12, + block_hash: "0xblock12".to_owned(), + block_timestamp: 1_710_000_222, + }, + true, + &[base, metadata], + ) + .expect("apply events"); + + let agent = storage + .get_agent(84532, "1") + .expect("get agent") + .expect("agent"); + assert_eq!(agent.selected_metadata["metadata.best_val_bpb"], "1.111"); + assert_eq!( + agent.raw_metadata["onchain_indexed"]["metadata.best_val_bpb"], + "1.111" + ); + } + + #[test] + fn list_agents_supports_filters_and_search() { + let (_dir, storage) = temp_storage(); + let first = registered_event( + "1", + "https://worker-a.example.com/.well-known/agent-registration.json", + 1_710_000_000, + registration( + "GPU Worker Alpha", + "https://worker-a.example.com/services/autoresearch-worker", + ), + ); + let second = StoredEvent { + token_id: "2".to_owned(), + tx_hash: "0xtx20".to_owned(), + block_hash: "0xblock20".to_owned(), + block_number: 20, + log_index: 0, + block_timestamp: 1_710_000_500, + tx_index: 0, + chain_id: 84532, + payload: EventPayload::Registered { + owner_address: "0x9999999999999999999999999999999999999999".to_owned(), + uri: "https://worker-b.example.com/.well-known/agent-registration.json".to_owned(), + registration_json: Some(registration( + "Searchable Beta", + "https://worker-b.example.com/services/reviewer", + )), + fetch_error: None, + }, + }; + + storage + .apply_events( + 84532, + &BlockCursor { + block_number: 20, + block_hash: "0xblock20".to_owned(), + block_timestamp: 1_710_000_500, + }, + true, + &[first, second], + ) + .expect("apply events"); + + let filtered = storage + .list_agents(&ListOptions { + page: 1, + limit: 10, + chain_id: Some(84532), + protocol: Some("OASF".to_owned()), + search: Some("searchable".to_owned()), + sort_by: Some("name".to_owned()), + sort_order: Some("asc".to_owned()), + owner_address: None, + }) + .expect("list agents"); + + assert_eq!(filtered.items.len(), 1); + assert_eq!(filtered.items[0].name, "Searchable Beta"); + assert_eq!(filtered.pagination.total, 1); + } + + #[test] + fn health_defaults_to_starting_and_becomes_ready() { + let (_dir, storage) = temp_storage(); + assert_eq!( + storage.health_snapshot().expect("health").status, + "starting" + ); + + storage + .apply_events( + 84532, + &BlockCursor { + block_number: 10, + block_hash: "0xblock10".to_owned(), + block_timestamp: 1_710_000_000, + }, + true, + &[registered_event( + "1", + "https://worker.example.com/.well-known/agent-registration.json", + 1_710_000_000, + registration( + "GPU Worker Alpha", + "https://worker.example.com/services/autoresearch-worker", + ), + )], + ) + .expect("apply events"); + + let health = storage.health_snapshot().expect("health"); + assert_eq!(health.status, "ok"); + assert!(health.ready); + assert_eq!(health.latest_indexed_block, Some(10)); + } + + #[test] + fn current_cursor_persists_across_restarts() { + let (dir, storage) = temp_storage(); + storage + .apply_events( + 84532, + &BlockCursor { + block_number: 55, + block_hash: "0xblock55".to_owned(), + block_timestamp: 1_710_000_777, + }, + true, + &[], + ) + .expect("apply cursor"); + + let reopened = Storage::new( + dir.path().join("indexer.db"), + "0x8004a818bfb912233c491871b3d84c89a494bd9e", + ); + reopened.init().expect("reopen init"); + assert_eq!(reopened.current_cursor().expect("cursor"), Some(55)); + } + + #[test] + fn backfill_range_respects_existing_cursor() { + assert_eq!(initial_backfill_range(None, 25, 5), Some(5..=25)); + assert_eq!(initial_backfill_range(Some(24), 25, 5), Some(25..=25)); + assert_eq!(initial_backfill_range(Some(25), 25, 5), None); + } +} diff --git a/tests/skills_smoke_test.py b/tests/skills_smoke_test.py index 7c454a72..9ad79d80 100644 --- a/tests/skills_smoke_test.py +++ b/tests/skills_smoke_test.py @@ -18,6 +18,7 @@ SKILLS_DIR = "/data/.openclaw/skills" RPC = os.path.join(SKILLS_DIR, "ethereum-networks", "scripts", "rpc.py") KUBE = os.path.join(SKILLS_DIR, "obol-stack", "scripts", "kube.py") +WORKER = os.path.join(SKILLS_DIR, "autoresearch-worker", "scripts", "worker_api.py") passed = 0 failed = 0 @@ -276,6 +277,31 @@ def test_network_summary(): test("distributed-validators/network_summary", test_network_summary) +# ────────────────────────────────────────────── +# autoresearch-worker tests +# ────────────────────────────────────────────── +print("\n\033[1m--- autoresearch-worker ---\033[0m") + + +def test_autoresearch_worker_files(): + for f in ["SKILL.md", "scripts/worker_api.py", "references/worker-api.md"]: + path = os.path.join(SKILLS_DIR, "autoresearch-worker", f) + assert os.path.isfile(path), f"missing: {f}" + + +test("autoresearch-worker/files_exist", test_autoresearch_worker_files) + + +def test_autoresearch_worker_help(): + rc, out, err = run(["python3", WORKER, "--help"]) + assert rc == 0, f"exit {rc}: {err}" + assert "experiment" in out.lower(), f"missing experiment help text in: {out}" + assert "--repo" in out, f"missing --repo option in: {out}" + + +test("autoresearch-worker/help", test_autoresearch_worker_help) + + # ────────────────────────────────────────────── # Summary # ────────────────────────────────────────────── diff --git a/tests/test_autoresearch_worker.py b/tests/test_autoresearch_worker.py new file mode 100644 index 00000000..d9921a1d --- /dev/null +++ b/tests/test_autoresearch_worker.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +import importlib.util +import os +import sys +import tempfile +import unittest +from pathlib import Path + +MODULE_PATH = Path(__file__).resolve().parents[1] / "internal" / "embed" / "skills" / "autoresearch-worker" / "scripts" / "worker_api.py" + + +def load_worker_module(): + spec = importlib.util.spec_from_file_location("autoresearch_worker_api", MODULE_PATH) + module = importlib.util.module_from_spec(spec) + assert spec.loader is not None + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +class WorkerHelpersTest(unittest.TestCase): + def test_extract_val_bpb_from_plaintext(self): + worker = load_worker_module() + text = "training complete\nval_bpb: 1.2345\n" + self.assertEqual(worker.extract_val_bpb(text), 1.2345) + + def test_extract_val_bpb_from_json_metrics(self): + worker = load_worker_module() + text = '{"metrics": {"val_bpb": 0.9987}}' + self.assertEqual(worker.extract_val_bpb(text), 0.9987) + + def test_extract_val_bpb_from_results_tsv(self): + worker = load_worker_module() + with tempfile.TemporaryDirectory() as td: + results = Path(td) / "results.tsv" + results.write_text( + "commit_hash\tval_bpb\tstatus\tdescription\n" + "aaa\t1.250\tkeep\tbaseline\n" + "bbb\t1.111\tkeep\tbetter\n" + ) + self.assertEqual(worker.extract_val_bpb("", Path(td)), 1.111) + + def test_find_artifact_prefers_latest_checkpoint_like_file(self): + worker = load_worker_module() + with tempfile.TemporaryDirectory() as td: + root = Path(td) + a = root / "older.pt" + b = root / "newer.gguf" + a.write_text("x") + b.write_text("y") + os.utime(a, (1, 1)) + os.utime(b, (2, 2)) + self.assertEqual(worker.find_artifact(root), b) + + def test_choose_best_result_prefers_lower_val_bpb(self): + worker = load_worker_module() + old = {"val_bpb": 1.2, "experiment_id": "old"} + new = {"val_bpb": 1.1, "experiment_id": "new"} + self.assertEqual(worker.choose_best_result(old, new)["experiment_id"], "new") + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_sell_registration_metadata.py b/tests/test_sell_registration_metadata.py new file mode 100644 index 00000000..cbb316b8 --- /dev/null +++ b/tests/test_sell_registration_metadata.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +import importlib.util +import sys +import unittest +from pathlib import Path + +MODULE_PATH = Path(__file__).resolve().parents[1] / "internal" / "embed" / "skills" / "sell" / "scripts" / "monetize.py" + + +def load_monetize_module(): + spec = importlib.util.spec_from_file_location("sell_monetize", MODULE_PATH) + module = importlib.util.module_from_spec(spec) + assert spec.loader is not None + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +class RegistrationMetadataTest(unittest.TestCase): + def test_build_registration_doc_includes_custom_metadata(self): + mod = load_monetize_module() + spec = { + "type": "http", + "path": "/services/autoresearch-worker", + "payment": {"price": {"perHour": "0.50"}}, + "registration": { + "name": "GPU Worker Alpha", + "skills": ["machine_learning/model_optimization"], + "domains": ["technology/artificial_intelligence/research"], + "metadata": { + "gpu": "A100-80GB", + "framework": "pytorch", + "best_val_bpb": "1.234", + "total_experiments": "42", + }, + }, + } + + doc = mod.build_registration_doc(spec, "autoresearch-worker", "1789", "http://obol.stack:8080") + self.assertEqual(doc["name"], "GPU Worker Alpha") + self.assertIn("metadata", doc) + self.assertEqual(doc["metadata"]["gpu"], "A100-80GB") + self.assertEqual(doc["metadata"]["best_val_bpb"], "1.234") + self.assertTrue(any(s.get("name") == "OASF" for s in doc["services"])) + + def test_build_registration_doc_includes_provenance(self): + mod = load_monetize_module() + spec = { + "type": "inference", + "path": "/services/my-model", + "payment": {"price": {"perRequest": "0.001"}}, + "provenance": { + "framework": "autoresearch", + "metricName": "val_bpb", + "metricValue": "0.9973", + "experimentId": "abc123", + "trainHash": "sha256:deadbeef", + "paramCount": "50000000", + }, + "registration": { + "name": "My Model", + }, + } + + doc = mod.build_registration_doc(spec, "my-model", "99", "http://obol.stack:8080") + self.assertIn("provenance", doc) + self.assertEqual(doc["provenance"]["framework"], "autoresearch") + self.assertEqual(doc["provenance"]["metricValue"], "0.9973") + self.assertEqual(doc["provenance"]["trainHash"], "sha256:deadbeef") + self.assertEqual(doc["provenance"]["paramCount"], "50000000") + + def test_build_indexed_metadata_includes_registration_metadata(self): + mod = load_monetize_module() + spec = { + "type": "http", + "registration": { + "metadata": { + "gpu": "A100-80GB", + "best_val_bpb": "1.234", + } + }, + } + entries = mod.build_indexed_metadata(spec) + self.assertEqual(entries["service.type"], b"http") + self.assertEqual(entries["metadata.gpu"], b"A100-80GB") + self.assertEqual(entries["metadata.best_val_bpb"], b"1.234") + + +if __name__ == "__main__": + unittest.main()