Performance Evaluation

How provider performance is evaluated

Performance certificates measure and certify provider hardware quality. Each provider runs a benchmark on startup, producing a signed certificate with attested timestamps from trusted timekeepers. The certificate is attached to every lease the provider accepts.

Certificate Overview

A performance certificate proves a provider's machine completed a known benchmark in a measured amount of time. The start and end times come from network timekeepers, not the provider's clock.

Provider                     Timekeepers                    Network
────────                     ───────────                    ───────

1. Request start attestation
         ─────────────────►
                             Attest start timestamp
         ◄─────────────────

2. Derive seed from start attestation
   Run benchmark (~60s):
     Phase 1: sequential hash chain (CPU)
     Phase 2: memory table + random reads (memory)

3. Request end attestation
   (different identifier: the provider address
    with its middle 16 hex chars zeroed, so the
    30s per-identifier rate limit does not bite)
         ─────────────────►
                             Attest end timestamp
         ◄─────────────────

4. Assemble certificate:
   seed + work proof + merkle roots of attestations
   Sign with provider's node key

5. Publish via gossip
         ──────────────────────────────────────►
                                                All nodes cache

Certificate Structure

Certificates use a compact format with attestation merkle roots (~700 bytes) instead of full attestation arrays (~2.6 KB):

type Certificate struct {
    Provider             string  // ed25519 pubkey hex
    Seed                 string  // sha256(start_median || provider_pubkey)
    WorkloadVersion      uint64  // benchmark algorithm version (currently 3)
    Iterations           uint64  // hash chain length (375,000,000)
    WorkProof            string  // sha256(cpu_hash || memory_proof)
    Score                float64 // 1.0 / attested_elapsed_seconds

    StartAttestationRoot string  // merkle root of start attestations
    EndAttestationRoot   string  // merkle root of end attestations
    StartMedian          int64   // median start timestamp (nanos)
    EndMedian            int64   // median end timestamp (nanos)

    IssuedAt             int64   // == StartMedian
    ExpiresAt            int64   // issued_at + 7 days

    PriceMultiplierMilli uint64  // scales baseline lease cost, x1000

    Signature            string  // provider signs all fields
    Hash                 string  // sha256 of certificate content
}

Price multiplier

PriceMultiplierMilli scales the baseline lease cost: 1000 = 1.000×, 2500 = 2.500×. It is bounded to [500, 10000] (0.5× to 10×) as a sanity guard, and a certificate outside that range is refused at issue and rejected on verification. Because the lease block names a specific certificate, the multiplier is locked at offer time and the provider must accept with the same certificate hash — the consumer cannot be re-priced after committing.

[!WARNING] Non-baseline multipliers are simulation-only A provider sets its multiplier with --price-multiplier, defaulting to 1000. Anything other than the baseline is a simulation feature (#297): the node logs a warning when it issues such a certificate, and production use is gated on the anti-cheat work in #213/#214/#215. Nothing yet verifies that a higher multiplier is earned.

The merkle roots use RFC 6962 domain separation — leaves are prefixed 0x00 and internal nodes 0x01 — so a 32-byte leaf cannot be substituted for an internal node.

Benchmark

The benchmark has two phases, both deterministic and sequential.

Phase 1: Sequential Hash Chain (CPU)

375 million SHA-256 iterations where each depends on the previous:

h[0] = sha256(seed)
h[1] = sha256(h[0])
...
h[N] = sha256(h[N-1])

Targets ~60 seconds on mid-range hardware. Cannot be parallelised.

Phase 2: Memory Table (Memory)

After the hash chain, build a 256 MB lookup table (8,388,608 × 32-byte entries):

table[0] = sha256(cpu_hash || 0)
table[1] = sha256(cpu_hash || 1)
...
table[M-1] = sha256(cpu_hash || M-1)

Then perform 1,000,000 seed-derived random reads with sequential dependency:

idx[0] = cpu_hash mod M
read[0] = table[idx[0]]
idx[1] = read[0] mod M
read[1] = table[idx[1]]
...

The memory proof is the hash of all reads. This requires holding the full table in memory — outsourcing over a network adds round-trip latency per random read, making it ~10,000× slower than local access.

Final Proof

work_proof = sha256(cpu_hash || memory_proof)

Scoring

score = 1.0 / attested_elapsed_seconds

Higher score = faster hardware. The elapsed time comes from the median of timekeeper attestations, not the provider's clock.

Reference scores

Benchmark time

Score

Hardware tier

10s

0.100000

High-end dedicated

30s

0.033333

Fast server

59s

0.016951

bm1 testnet (4 vCPU)

62s

0.016172

bm2 testnet (8 vCPU)

120s

0.008333

Slow / shared hosting

300s

0.003333

Extremely slow

Anti-Cheat Properties

Property

Mechanism

No pre-computation

Seed derived from attested start timestamp + provider pubkey. Unknown until attestation received.

No parallelisation

Sequential hash chain — each step depends on previous output.

No outsourcing

Memory-hard phase requires 256 MB table in local RAM. Network round-trip per random read makes outsourcing 10,000× slower.

No time manipulation

Start and end timestamps from trusted timekeepers at the network's configured threshold (2 of 3 on the current testnet). Provider cannot claim faster time.

Identity binding

Certificate signed by provider's ed25519 key. Seed includes provider pubkey.

Expiry

7-day validity. Must re-certify to continue accepting leases.

Iteration count

Verified: must equal BenchmarkIterations (375M). Cannot run fewer for speed.

Lease Integration

Both the lease block and the lease_accept block carry a certificate_hash, and they must be the same one.

On the lease block the ledger validates:

  1. certificate_hash is non-empty
  2. Certificate exists in the gossip cache
  3. Certificate's Provider field matches the lease's destination
  4. Certificate is not expired at the lease block's timestamp
  5. amount equals LeaseCost(...) computed with the certificate's PriceMultiplierMilli

On the lease_accept block the ledger validates:

  1. certificate_hash is non-empty and equals the hash the lease block referenced (the rate lock)
  2. Certificate's Provider field matches the accepting provider
  3. Certificate is not expired at the attested start time
// In validateAndAddLeaseAccept:
certInfo := l.certificateLookupFn(b.CertificateHash)
if certInfo.Provider != b.Account { reject }
if startTime > certInfo.ExpiresAt { reject }

The lease record stores the certificate hash for audit:

{
  "lease_hash": "abc123...",
  "state": "accepted",
  "provider": "423463...",
  "certificate_hash": "69af25...",
  "settled": false
}

Certificate Gossip

Certificates are broadcast via GossipSub topic xe/certificates. All nodes cache received certificates indexed by provider account.

  • Publishing: provider broadcasts after generation, and re-publishes every 5 minutes so peers that joined later still see it
  • Validation (VerifyCertificate): hash, provider signature, expiry, positive score, workload_version, iterations, and price_multiplier_milli within [500, 10000]
  • Deduplication: newer IssuedAt timestamp replaces older certificates
  • Persistence: certificates are chain data — their hash is bound into lease block hashes — so the cache is written to the store and reloaded at boot, keeping cold sync of historical leases possible
  • API: GET /certificate (local provider) and GET /certificate/{provider} (any cached cert)

Constants

BenchmarkIterations    = 375_000_000        // ~60s on mid-range hardware
MemoryTableSize        = 8_388_608          // 256 MB (entries × 32 bytes)
MemoryReads            = 1_000_000          // sequential-dependency reads
WorkloadVersion        = 3                  // algorithm version
CertificateValidity    = 7 * 24 * time.Hour // 7 days

PriceMultiplierMin     = 500                // 0.500×
PriceMultiplierMax     = 10000              // 10.000×
PriceMultiplierDefault = 1000               // 1.000×

File Reference

File

Description

perf/benchmark.go

Hash chain + memory-hard benchmark

perf/certificate.go

Certificate struct, signing, verification, merkle roots

node/perf.go

Certificate generation on provider startup

net/gossip.go

CertificateGossip (xe/certificates topic)

core/ledger.go

Certificate validation in lease and lease_accept

api/vm.go

GET /certificate and GET /certificate/{provider}

Future Work

  • Performance-weighted pricing — score × resources × duration (#246)
  • Checkpoint spot-checking — verify chain segments without full re-run (#247)
  • Heartbeat micro-benchmarks — detect degradation during leases (#249)
  • Certificate renewal — automatic re-benchmark before expiry (#250)
  • Anti-cheat — outsourcing detection (#213), collusion prevention (#214), boost detection (#215)