Service Challenge Integration
Practical guide to integrating MatMul service challenges for API rate limiting, spam prevention, and AI endpoint gating.
这份参考属于 BTX 构建者部署路径的一部分。
回到 Develop 微站,可以把这页重新放回 AI 代理 CAPTCHA、后量子身份和开发启动包的完整叙事中理解。
This guide walks through integrating BTX MatMul service challenges into your application. Service challenges let you require computational proof-of-work from clients before admitting requests — useful for API rate limiting, signup spam prevention, and AI inference endpoint gating.
For the RPC reference, see Service Challenge RPCs.
Architecture
The service challenge flow involves three participants: your application server, a BTX node, and the client.
┌──────────┐ 1. Issue challenge ┌──────────┐
│ │ ──────────────────────── ▶ │ │
│ Your │ (getmatmulservice │ BTX │
│ App │ challenge) │ Node │
│ Server │ │ :19334 │
│ │ ◀ ──────────────────────── │ │
│ │ challenge envelope │ │
└────┬─────┘ └────┬─────┘
│ │
│ 2. Send envelope to client │
▼ │
┌──────────┐ │
│ Client │ 3. Solve MatMul puzzle │
│ │ (local computation) │
└────┬─────┘ │
│ │
│ 4. Return proof (nonce + digest) │
▼ │
┌──────────┐ 5. Redeem proof ┌────┴─────┐
│ Your │ ──────────────────────── ▶ │ BTX │
│ App │ (redeemmatmulservice │ Node │
│ Server │ proof) │ │
│ │ ◀ ──────────────────────── │ │
│ │ valid/redeemed/reason │ │
└──────────┘ └──────────┘ Key properties of this flow:
- Each challenge is one-shot — once redeemed, replay returns
already_redeemed - Challenges are domain-bound to a purpose, resource, and subject
- Work is anchored to current chain state, so pre-computation is time-limited
- Verification is cheap relative to the solve cost
The Core Flow
Step 1: Issue a Challenge
Call getmatmulservicechallenge with bindings for your use case:
curl --silent --user "$BTX_RPC_USER:$BTX_RPC_PASSWORD" \
--data-binary '{
"jsonrpc":"1.0",
"id":"issue",
"method":"getmatmulservicechallenge",
"params":["rate_limit","signup:/v1/messages","user:[email protected]",2,300,0.25,0.75]
}' \
-H 'content-type: text/plain;' \
http://127.0.0.1:19334/ The parameters are:
| Position | Name | Description |
|---|---|---|
| 1 | purpose | Challenge domain: rate_limit, api_gate, ai_inference_gate |
| 2 | resource | The protected resource, e.g. signup:/v1/messages |
| 3 | subject | The requester identity, e.g. user:[email protected] or ip:203.0.113.5 |
| 4 | target_solve_time_s | Target solve time in seconds (difficulty calibration) |
| 5 | expiry_s | Challenge validity window in seconds |
| 6 | min_difficulty_factor | Minimum difficulty relative to network (0.0-1.0) |
| 7 | max_difficulty_factor | Maximum difficulty relative to network (0.0-1.0) |
The response contains a challenge envelope with:
challenge_id— unique identifier for replay protectionissued_at/expires_at— validity windowheader_context— chain-anchored header for deterministic seedstarget— difficulty target the client must meet- MatMul seeds derived from the
challenge_id
Step 2: Client Solves the Challenge
Send the challenge envelope to the client. The client performs the MatMul computation locally — multiplying matrices, hashing the result, and searching for a nonce whose digest meets the target. The output is a nonce64_hex and digest_hex.
Step 3: Redeem the Proof
When the client returns the proof, call redeemmatmulserviceproof:
curl --silent --user "$BTX_RPC_USER:$BTX_RPC_PASSWORD" \
--data-binary '{
"jsonrpc":"1.0",
"id":"redeem",
"method":"redeemmatmulserviceproof",
"params":[CHALLENGE_ENVELOPE,"0000000000000001","DIGEST_HEX"]
}' \
-H 'content-type: text/plain;' \
http://127.0.0.1:19334/ Accept the request only when all three conditions hold:
valid = truereason = "ok"redeemed = true
Integration Patterns
Signup and Comment Spam Prevention
Require proof-of-work before accepting form submissions. This raises the cost of automated spam without blocking legitimate users.
| Binding | Recommended Value |
|---|---|
purpose | rate_limit |
resource | signup:/v1/messages or comment:/v1/posts/:id |
subject | user:<id> or ip:<bucket> |
expiry | 60-300 seconds |
target_solve_time_s | 1-3 seconds (high enough to price abuse, low enough for UX) |
Issue the challenge on form render or just before submission. Redeem on final submit, not on initial page load.
API Rate Limiting
Gate public API endpoints behind proof-of-work instead of (or alongside) API key quotas.
| Binding | Recommended Value |
|---|---|
purpose | api_gate |
resource | POST:/v1/inference or GET:/v1/search |
subject | api_key:<id> or tenant:<id> |
Use shorter expiries and redeem on request receipt so stale pre-computation is less useful.
AI Inference Endpoint Gating
Protect expensive AI model endpoints by requiring useful work before inference is admitted. This is effective for free-tier throttling, anonymous endpoint hardening, and protecting multimodal routes.
| Binding | Recommended Value |
|---|---|
purpose | ai_inference_gate |
resource | model:gpt-x|route:/v1/generate|tier:free |
subject | tenant:abc123 |
Scale puzzle cost by endpoint sensitivity, user trust level, or tenant risk score using getmatmulservicechallengeprofile to calibrate difficulty for a specific target solve time.
Reference Architectures
Develop 微站把 service challenge 的接入方式归纳为几种常见的路由形态。你可以把它们当作起始契约,再根据真实求解延迟、滥用类型和自身集群拓扑继续调优。
Profile: Public AI Inference API
Use this when the expensive thing is model execution itself and you want a machine-readable work gate in front of a free-tier or lightly trusted route.
| Binding | Recommended value |
|---|---|
purpose | ai_inference_gate |
resource | model:gpt-x|route:/v1/generate|tier:free |
subject | tenant:<id> or api_key:<id> |
target_solve_time_s | 2-4 seconds for free-tier or anonymous traffic |
expiry_s | 60-180 seconds so old envelopes lose value quickly |
| Redeem path | Start with redeemmatmulserviceproof at the front door, then move to redeemmatmulserviceproofs only when queued or shared-worker inference benefits from batching |
Issue the challenge before the request enters the expensive queue, persist challenge_id beside the upstream request id, and only add batch redemption when your worker pool actually benefits from it. Pair this profile with difficulty calibration as route cost or tenant risk changes.
Profile: Agent or Tool Gateway
Use this when a signed caller is already presenting a wallet identity, device token, or route scope and you want fresh work to guard the expensive or privileged action behind that gateway.
| Binding | Recommended value |
|---|---|
purpose | api_gate |
resource | tool:calendar.write|route:/mcp/tools/calendar.write |
subject | wallet:agent-42 or device:devtok_abc123 |
target_solve_time_s | 1-2 seconds for interactive calls, higher for privileged routes |
expiry_s | 30-120 seconds so the signature and work remain tightly coupled |
| Redeem path | Use redeemmatmulserviceproof at the gateway edge for one request at a time |
Keep the resource specific to the exact tool or route. Do not let a generic gateway proof float across unrelated capabilities. This profile maps cleanly onto stacks that already track role, scope, and device trust, so pair it with the protected route contract and its header semantics.
Profile: Anonymous Public Submission
Use this when you need friction for signup, comments, intake forms, or other open routes where the caller may have no durable account yet.
| Binding | Recommended value |
|---|---|
purpose | rate_limit |
resource | signup:/v1/messages or comment:/v1/posts/:id |
subject | ip:203.0.113.0/24 or user:[email protected] |
target_solve_time_s | 1-3 seconds so honest users can finish while spam cost rises materially |
expiry_s | 60-300 seconds, with redemption only on the final submit |
| Redeem path | Use redeemmatmulserviceproof on final submit unless accepted work is later batched for moderation or ingestion |
Start from the core flow and issue the challenge just before submission or alongside short-lived draft state. Avoid burning proofs on read-only page views, and if accepted work later fans into queues preserve the original redeem result instead of trying to replay the same proof downstream.
Batch Redemption
High-throughput gateways should use redeemmatmulserviceproofs to redeem multiple proofs in a single RPC round trip:
curl --silent --user "$BTX_RPC_USER:$BTX_RPC_PASSWORD" \
--data-binary '{
"jsonrpc":"1.0",
"id":"redeem-batch",
"method":"redeemmatmulserviceproofs",
"params":[[
{
"challenge": "CHALLENGE_A",
"nonce64_hex": "0000000000000001",
"digest_hex": "DIGEST_A"
},
{
"challenge": "CHALLENGE_B",
"nonce64_hex": "0000000000000002",
"digest_hex": "DIGEST_B"
}
]]
}' \
-H 'content-type: text/plain;' \
http://127.0.0.1:19334/ Batch semantics:
- Processing order is preserved
- The operation is sequential, not all-or-nothing
- Duplicate or already-redeemed proofs return
already_redeemed - Unknown cross-node challenges return
unknown_challenge
The response includes count, valid, invalid, by_reason, and ordered per-item results.
Difficulty Calibration
Use getmatmulservicechallengeprofile to get a challenge shape for a custom solve-time target. This lets you dynamically price work based on:
- Endpoint sensitivity (a public comments form vs. a free-tier AI image endpoint)
- User trust level (verified accounts get easier puzzles)
- Tenant risk score
- Live network conditions (via
getdifficultyhealth)
Example: a public comments endpoint might target 1 second of solve time, while a free-tier AI image generation route might target 4 seconds.
Protected Route Contract
Once you move beyond the primitives, treat BTX as a first-party contract between your edge or gateway layer and the issuing BTX node. The goal is a deterministic request lifecycle: issue, solve, redeem, admit or reject.
Issue Contract
| Phase | Expectation | Operator Note |
|---|---|---|
| Issue | Call getmatmulservicechallenge using route-specific purpose, resource, and subject bindings. | Do this at the edge or on the first admission hop, not deep inside the expensive handler. |
| Return envelope | Return challenge_id, expires_at, target, and the chain-bound envelope to the caller. | Persist the issuing-node identity beside the envelope if your fleet has more than one BTX node. |
| Redeem | Redeem on the same node that issued the challenge unless you have a shared challenge store. | This is the operational reason sticky routing matters in multi-node setups. |
Protected-Route Headers
The exact transport is up to your gateway, but this is the recommended BTX-owned contract for protected HTTP routes:
| Header | Required | Purpose |
|---|---|---|
Authorization | Optional but recommended | Caller signature, bearer token, or gateway auth material that identifies who is asking for service. |
X-Device-Token | Optional | Gateway or device-trust token when your stack already distinguishes device posture from caller identity. |
X-BTX-Challenge-Id | Required | The challenge envelope identifier that lets the gateway and BTX node resolve the live challenge state. |
X-BTX-Proof-Nonce | Required | The client-solved nonce64_hex value returned by the local MatMul solve. |
X-BTX-Proof-Digest | Required | The resulting digest_hex that is checked against the challenge target on redeem. |
Failure Semantics
| Condition | What it means | Recommended action |
|---|---|---|
missing_proof | The caller reached a protected route without the BTX proof headers. | Reject immediately and return a fresh issue path or challenge envelope. |
unknown_challenge | The redeeming node does not know the challenge, usually because of sticky routing drift or a node restart. | Reissue from the current node and inspect sticky routing before retrying expensive routes. |
already_redeemed | The challenge was already consumed, which usually means replay or double-submit behavior. | Reject as a replay attempt; do not auto-reissue on the same request. |
expired or past expires_at | The caller solved too slowly or the envelope sat in a queue too long. | Reject, issue a new challenge, and reconsider expiry_s or the target solve time for that route. |
valid = false or reason != "ok" | The proof does not satisfy the current challenge or target. | Reject the request and only reissue if the user experience genuinely benefits from another attempt. |
Route Tuning
| Route class | target_solve_time_s | expiry_s | Difficulty band |
|---|---|---|---|
| Public signup or comments | 1-2 | 60-120 | 0.15-0.45 |
| Anonymous search or text API | 2-3 | 60-180 | 0.20-0.60 |
| Free-tier AI inference | 2-4 | 120-300 | 0.25-0.75 |
| Expensive multimodal or batch work | 4-8 | 180-300 | 0.35-0.90 |
Use getmatmulservicechallengeprofile and getdifficultyhealth together when tuning these values. The correct answer depends on both route cost and current network conditions.
Monitoring Checklist
- Track
getdifficultyhealthbefore changing solve targets so you are not tuning against a blind difficulty view. - Alert on spikes in
unknown_challenge; they usually indicate sticky routing failures or node restarts. - Alert on rising
already_redeemedrates; they often indicate replay or duplicate-submit patterns. - Watch expiry rate and median solve duration together. If honest callers are expiring, loosen
expiry_sor lower route cost.
Deployment Considerations
Single-Node Deployment
The simplest deployment runs issuance and redemption on the same BTX node:
- Issue with
getmatmulservicechallenge - Redeem on the same node with
redeemmatmulserviceproof - Issued challenge state lives in the node's local persistent registry by default
- The default registry path is
matmul_service_challenges.datunder the network datadir - Restarts preserve outstanding challenge state as long as that registry remains available
This is production-ready for most use cases.
Multi-Node Deployment
The redeem path still depends on the configured issued-challenge registry. This means:
- Cross-node redemption is rejected as
unknown_challengewhen nodes do not share registry state - Horizontally scaled frontends still need sticky routing unless nodes share the same registry file
- You can point multiple BTX nodes at the same
-matmulservicechallengefile=<file>path to share issuance and redemption state
For production multi-node deployments, either keep session affinity in front of node-local registries or configure a shared challenge registry file across the service nodes that issue and redeem proofs.
Verify vs. Redeem
BTX provides two verification paths:
| RPC | Replay Safe? | Use When |
|---|---|---|
verifymatmulserviceproof | No | Diagnostics, offline checking, debugging |
redeemmatmulserviceproof | Yes | Production admission control |
Always use redeem for real admission control. Stateless verify alone does not prevent replay.
What BTX Does and Does Not Prove
BTX proves that useful matrix-multiplication work was performed against a BTX-defined challenge and that the resulting digest met the required target.
BTX does not prove that:
- A specific AI model produced a specific output
- A claimed inference trace was faithful
- An application request should be authorized for business reasons
For semantic proof-of-inference, pair BTX admission control with a verifiable inference layer such as Jolt Atlas, EZKL, or RISC Zero.
Comparison with Other Defenses
| Mechanism | Proves Work | Cheap to Verify | Reusable for AI Infra | Good for Spam |
|---|---|---|---|---|
| BTX service challenge | Yes | Yes | Yes | Yes |
| Hashcash-style hash puzzle | Yes | Yes | No | Yes |
| CAPTCHA | No | N/A | No | Sometimes |
| API key / auth only | No | Yes | N/A | Weak |
| Verifiable inference layer | Execution only | Expensive | Sometimes | Indirectly |