Skip to content

v1.4.0

Latest

Choose a tag to compare

@austinabell austinabell released this 01 Apr 16:33
66fabe2

Proving Node v1.4

Proving Node v1.4 increases prover revenues, reduces operating costs, and improves stability across the board. This is a targeted release — bigger changes are coming in 2.0.

Highlights

1. Up to 67% more revenue with TaskDB optimizations

Stale and orphaned tasks were accumulating in the TaskDB over time, degrading performance and cutting into proving throughput. A configurable cleanup process now prevents this buildup automatically — running tasks and in-progress work are unaffected. This fix came directly from prover feedback. If you're running into issues or have ideas, join the Provers Telegram, we're listening.

2. Pricing improvements

Cross-currency profitability comparison across ETH, USD, and ZKC is now reliable and accurate. Provers can evaluate job profitability uniformly regardless of how proof requests are priced — making it easier to pick the most profitable work.

3. Up to 95% RPC cost reduction (experimental for this release, default next)

We've reworked the RPC logic to use more efficient methods, significantly reducing the number of calls your node makes. Public RPCs are now viable for dev and testing environments, lowering entry barriers for new operators and simplifying your test setups.

  • Redundant state polling calls reduced
  • Public RPCs viable for development and testing environments

4. Upgrade process improvements

We've improved how the Broker handles version compatibility. The Broker now validates prover versions against minimum requirements and surfaces clear upgrade instructions when an update is needed — so you're never running an outdated version without knowing it.


Deep Dive

Dynamic USD-Based Pricing

What Changed

Broker pricing has been simplified to a single min_mcycle_price that can be denominated in USD. Previously, operators had to maintain separate prices for ETH-based orders (min_mcycle_price in ETH) and collateral-based secondary fulfillments (min_mcycle_price_collateral_token in ZKC). Now you can set one USD price and the broker handles everything else.

Additionally both Primary Fulfillment ("LockAndFulfill", ETH-priced) and Secondary Fulfillment ("FulfillAfterLockExpire", ZKC-priced) orders can now be evaluated against the same USD baseline, making it straightforward to reason about whether orders are worth proving.

The minimal pricing config required is now:

[market]
min_mcycle_price = "0.00003 USD"
max_collateral = "10 USD"

Appending USD to any pricing value now denominates it in dollars. A built-in price oracle fetches live ETH/USD and ZKC/USD rates and converts your USD price to the appropriate token at runtime:

Secondary Fulfillment: expected_probability_win_secondary_fulfillment

When a prover's lock expires, the slashed collateral becomes a reward for any prover that fulfills the order. However, multiple provers may race to fulfill it, so you're not guaranteed to win. The expected_probability_win_secondary_fulfillment setting (default: 50) accounts for this by discounting the collateral reward before comparing it against your min_mcycle_price.

Formula used: effective_reward = collateral_reward * expected_probability / 100. The broker then computes the per-mcycle price from this effective reward and compares it against your min_mcycle_price (converted to ZKC).

Example: Suppose your broker is configured with:

min_mcycle_price = "0.00003 USD"
expected_probability_win_secondary_fulfillment = 50

Consider a typical Signal order (0x1734d...f4c) -- ~50,850 Mcycles, 100 ZKC collateral locked. Assume this was locked and not fulfilled, and so is available for secondary fulfillment:

  • Collateral reward: 100 ZKC (= $10.00 at $0.10/ZKC)
  • Effective reward at 50%: 50 ZKC (= $5.00)
  • Effective Mcycle price: $5.00 / 50,850 ≈ $0.0000983/Mcycle
  • min_mcycle_price: $0.00003/Mcycle
  • Accept -- $0.0000983 > $0.00003

Tuning the probability: You can tune the expected value of secondary fulfillment using the expected_probability_win_secondary_fulfillment

# Aggressive: you believe you will win lots of proof races, apply a smaller discount than the default 50%:
expected_probability_win_secondary_fulfillment = 80

# Prioritize secondary fulfillments: boost reward above 100% to aggressively
# pursue secondary fulfillment orders
expected_probability_win_secondary_fulfillment = 150

Recommended Configuration

[market]
# Single USD price for all order types
min_mcycle_price = "0.00003 USD"

# Discount factor for secondary fulfillment proof races (percentage)
# 50 = expect to win half the races, so require 2x the minimum to be profitable
expected_probability_win_secondary_fulfillment = 50

# Max collateral the broker will lock (also supports USD)
max_collateral = "10 USD"

Backward Compatibility

Existing broker.toml files will continue to work without any changes. Plain numbers without a currency suffix default to their previous behavior:

  • min_mcycle_price defaults to ETH (e.g., "0.00000001" is treated as "0.00000001 ETH")
  • min_mcycle_price_collateral_token defaults to ZKC (e.g., "0.00005" is treated as "0.00005 ZKC")
  • max_collateral defaults to ZKC (e.g., "200" is treated as "200 ZKC")

Migration Guide

Before v1.4 -- two separate prices, native tokens:

[market]
min_mcycle_price = "0.00000001"                # implicitly ETH
min_mcycle_price_collateral_token = "0.00005"  # implicitly ZKC, separate value for secondary fulfillments
max_collateral = "200"                         # implicitly ZKC

In v1.4 -- single USD price:

[market]
min_mcycle_price = "0.00003 USD"
expected_probability_win_secondary_fulfillment = 50
max_collateral = "20 USD"

Per-Requestor and Per-Selector Pricing Overrides

What It Does

You can now override min_mcycle_price for specific requestor addresses, specific proof types (by 4-byte function selector), or a combination of both. This allows you to price different workloads differently without changing the global default.

Resolution Priority

When evaluating an order, overrides are resolved in this order (first match wins):

  1. by_requestor_proof_type -- most specific (requestor address + proof type selector)
  2. by_proof_type -- matches any order with this selector
  3. by_requestor -- matches any order from this address
  4. Global min_mcycle_price -- fallback if no overrides match

Configuration Example

[market]
min_mcycle_price = "0.00003 USD"

# Override pricing for a specific requestor (e.g., a partner with a negotiated rate)
[market.pricing_overrides.by_requestor]
"0x1234567890abcdef1234567890abcdef12345678" = "0.00001 USD"

# Override pricing for a specific proof type selector (e.g., Groth16)
[market.pricing_overrides.by_proof_type]
"0xdeadbeef" = "0.0005 USD"

# Most specific: override for a particular requestor + proof type combo
[market.pricing_overrides.by_requestor_proof_type]
"0x1234567890abcdef1234567890abcdef12345678:0xdeadbeef" = "0.0003 USD"

Overrides support the same currency formats as the global price (USD, ETH, or plain numbers).


Experimental RPC cost savings (eth_getBlockReceipts)

Experimental: This feature is opt-in and under active development. It may have edge cases on some RPC providers.

What It Does

The --experimental-rpc flag replaces the standard RPC polling mechanism on eth_getLogs with calls to eth_getBlockReceipts. This reduces steady-state RPC usage to roughly 2 requests per block, which on a chain like Base (2-second blocks) amounts to ~60 requests/minute compared to potentially hundreds with the standard path.

Our testing has found this can reduce RPC costs by up to 10x, and in some cases can enable the broker to be run using free RPC providers

How to Enable

Set BROKER_EXTRA_ARGS in your .env file (or export it before running just prover up):

BROKER_EXTRA_ARGS="--experimental-rpc"

Or if you run the broker directly from the binary:

broker --experimental-rpc

Improved Gas Fee Estimation

What Changed

Gas fee estimation has been overhauled to produce more consistent and accurate gas estimations across the broker. All internal calls to eth_gasPrice have been removed in favor of EIP-1559 compliant fee estimation.

Configuration

The broker ships with recommend defaults, however both modes are configurable in broker.toml:

[market]
# For sending transactions (default: "medium")
# Options: "low", "medium", "high", or custom
gas_priority_mode = "medium"

# For profitability calculations (default: custom 1x base + 20th percentile)
gas_estimation_priority_mode = { custom = {
    base_fee_multiplier_percentage = 100,
    priority_fee_multiplier_percentage = 100,
    priority_fee_percentile = 20.0,
    dynamic_multiplier_percentage = 0
} }

Broker Telemetry

What It Does

The broker now includes a lightweight telemetry service that reports operational metrics to the Boundless order stream. The goal is to help provers and the network run reliably. Telemetry enables the Boundless team to proactively identify issues, detect degraded performance, and assist with debugging before problems impact prover earnings.

Telemetry is enabled by default and designed with privacy in mind.

What Is Sent

Telemetry reports operational metrics only -- timing, counts, and outcome codes. It does not send any private data.

Specifically, the broker sends two periodic heartbeats:

Broker heartbeat (every 5 minutes):

  • Broker address and version
  • Uptime
  • Number of committed orders and pending preflights
  • Broker configuration (pricing settings, capacity limits, etc.)

Request heartbeat (every 60 seconds):

  • Per-request evaluation outcomes (locked, skipped, or fulfilled-after-lock-expire) with skip reason codes
  • Cycle counts and proof type
  • Timing breakdowns: preflight duration, proving time, aggregation time, submission time
  • Commitment decisions and estimated proving times

What Is NOT Sent

  • Guest program code or ELF binaries
  • Proof data, witness inputs, or proof outputs
  • Request images or program uploads
  • Private keys or signing material

Disabling Telemetry

If you prefer to keep telemetry local, set the mode to logsonly in your broker.toml. Events will still be logged locally at debug level but will not be sent to the Boundless team:

[market]
telemetry_mode = "logsonly"

What's Changed

Contract Improvements

  • depositCollateralTo and depositCollateralWithPermitTo in #1728
  • Replace string-based require messages with custom errors in #1727
  • Support on-chain querying of old proof requests in #1509

Broker Improvements

  • Tightest deadline prioritization mode in #1804
  • Enforce minimum broker version via on-chain VersionRegistry in #1776
  • Dynamic USD-based pricing for ETH and ZKC in #1540
  • Per-requestor and per-selector min_mcycle_price pricing overrides in #1663
  • Experimental L1Monitor using eth_getBlockReceipts in #1715
  • Optional broker-side telemetry service in #1725
  • ZKC auto top-up for external provers in #1746
  • Listen-only mode via --listen-only flag in #1677
  • Pre-lock check before lock tx in #1669
  • Consistent base fee and priority fee estimations in #1612
  • Reduce gas estimation overhead in #1742
  • Retry logic for download failures in #1797
  • Secondary fulfillment probability estimation and reward prioritization in #1633
  • Default prover preflight on request submission in #1526

SDK Improvements

  • Move market data types from indexer-api to boundless-market in #1690
  • Add trait-based price provider support in #1496

Bento Improvements

  • CPU agents for exec and auxiliary tasks in #1789
  • Recreate request_work function for task management in #1774

Infrastructure

  • Pre-built Docker images for broker and bento in #1674
  • Migrate distributor OFAC screening to Chainalysis Sanctions REST API in #1755

Bug Fixes

  • Fix double-scaling of external prover collateral balance in #1795
  • Exclude slashed orders from prover fees_earned calculation in #1802
  • Fix order-stream pipeline reliability in #1770
  • Pre-compute sort keys in order prioritization to prevent panic in #1740

Full Changelog: v1.2.2...v1.4.0