Skip to content

Latest commit

 

History

History
100 lines (86 loc) · 5.76 KB

File metadata and controls

100 lines (86 loc) · 5.76 KB

gulltoppr — progress log

Running log of improvements to gulltoppr (the heimdall-rs ABI decompilation backend behind abi.ninja). Part of the larger effort to make abi.ninja usable by AI agents — see IDEATION.md in the abi-agent workspace for the overall plan and how gulltoppr fits in.


Round 3 — 2026-06-08 (decode_tx endpoint, version surfacing)

  • HEIMDALL_VERSION in /health. The Dockerfile sets it as a runtime env from the pinned build arg; /health now reports heimdall_version (e.g. 0.9.3). This is the source of truth because heimdall's own --version string lags its release tags (the 0.9.3 release binary self-reports 0.9.2). Defaults to "unknown" when the env isn't set (local dev).

  • GET /v1/decode/{tx_hash}?rpc_url=host — decode a transaction's calldata into { name, signature, inputs, decoded_inputs } ("what did this tx do"), via heimdall decode. Wrapped with provenance (source: "heimdall-decoded", cached, elapsed_ms). Cached by tx hash + rpc (calldata is immutable), and it rides the same coalescing path.

  • Refactored heimdall.rs: decompile_abi and decode_tx now share a private run_to_json(subcommand, target, …, output_file); read_output/find_nested are filename-generic.

  • Extracted cached_or_run() in main.rs — the coalescing/cache logic is now shared by resolve_abi and resolve_decode. Cache keys are namespaced (abi: / decode:) so the two operations never collide.

  • Added normalize_tx_hash validation (0x + 64 hex) and an InvalidTxHash error (400 invalid_tx_hash).

  • Tests: +7 (tx-hash validation, namespace collision, decode success/shape, decode caching, invalid hash). 32 total. Verified live against a real mainnet tx (decoded a transfer(address,uint256)).

Round 2 — 2026-06-08 (coalescing, tests, CI)

  • Request coalescing. Switched the cache from naive get-then-insert to moka's entry API (or_try_insert_with). N concurrent requests for the same uncached (address, rpc) now trigger a single decompile; the rest await that result. Failures are not cached. The X-Cache HIT/MISS flag stays accurate via Entry::is_fresh().
  • HTTP integration tests. Added a http_tests module (9 tests) that drives the real handlers + cache + subprocess wiring through actix_web::test, using a stub heimdall shell script (no network / no real heimdall). Covers: greet, health, invalid address (400), missing rpc_url (400), decompile success + cache HIT, /v1 shape, decompilation failure (502), no-bytecode (422), missing binary (503), and a concurrency test proving 5 simultaneous misses → 1 decompile.
  • CI. Added .github/workflows/ci.yml: cargo fmt --check, cargo clippy -D warnings, cargo test, and a Docker image build (validates the Dockerfile
    • pinned heimdall download). Tests need no heimdall binary.
  • Fixed dead deploy trigger. fly-deploy.yml watched main, but the repo's default branch is master, so auto-deploy never fired. Pointed it at master.
  • Fly health check. Added an [[http_service.checks]] probe on /health.
  • Extracted configure() (shared route wiring) and build_state() so main and the tests build the exact same app.

Verified: 25 tests pass, clippy clean with -D warnings.

Round 1 — 2026-06-08 (modernization)

  • Correctness: async decompile via tokio::process (no longer blocks Actix worker threads); configurable hard timeout; per-request temp dir output (auto-cleaned, no more output/ disk leak); address + rpc_url validation; configurable HEIMDALL_BIN (runs locally, not just at the hardcoded path).
  • Caching: in-memory moka cache keyed by (address, rpc_url), 24h TTL.
  • API: structured JSON errors with stable codes + correct statuses (400/422/502/503/504); provenance headers (X-Source/X-Cache/X-Elapsed-Ms); new GET /v1/{address} structured endpoint; real GET /health.
  • Freshness: bumped actix-web/cors/governor/env_logger; added serde_json, tokio, moka, tempfile. Rewrote the Dockerfile (multi-stage, pinned precompiled heimdall 0.9.3, trixie-slim runtime for glibc ≥ 2.39, no toolchain at runtime → ~191MB). Removed a 13MB binary mistakenly committed to the repo.
  • Structure: split main.rs into config / validation / heimdall / error modules; added unit tests.
  • The GET /{address} success contract stays backwards-compatible (raw ABI array).

Backlog / ideas (not yet done)

  • SSRF consideration. rpc_url lets a caller make the server fetch arbitrary hosts. Not hardened deliberately: blocking private/localhost addresses would break abi.ninja's localhost (chain 31337) decompilation. Revisit if the public instance needs it (e.g. an allowlist gated by env).
  • Self-destructed contracts. heimdall supports --etherscan-api-key to fetch creation bytecode for self-destructed contracts; could thread an env-configured key through.
  • Negative caching. Optionally cache "no bytecode" briefly to avoid re-running heimdall on repeated EOA lookups.
  • Metrics/observability. Expose decompile durations, cache hit rate, in-flight count (e.g. /metrics).
  • Agent-facing surface. Provenance/confidence is already in /v1; the broader resolution ladder (Etherscan → Sourcify → proxy → heimdall → 4byte) and the MCP/SDK wrappers live at the abi-agent project level, not here.
  • Raw calldata decode. heimdall decode also accepts a raw calldata hex string (no RPC needed). Could expose this (e.g. POST /v1/decode with a body) alongside the tx-hash variant — done in Round 3 for tx hashes only.
  • Transaction trace inspection. heimdall inspect decodes full traces + logs (richer than decode). A heavier "explain this tx" endpoint could build on it.