Service Challenges
MatMul-based proof-of-work challenges for API rate limiting, spam prevention, and AI endpoint gating.
هذه المرجعية جزء من طرح البناء في BTX.
ارجع إلى موقع Develop المصغر لتقرأ هذا الدليل داخل إطار CAPTCHA للوكلاء، وقصة الهوية ما بعد الكم، وحزمة المطورين.
Overview
BTX service challenges are one-shot MatMul proof-of-work envelopes for API admission, spam resistance, and AI endpoint gating. This page is the RPC contract: use it when you need the exact method signatures, response fields, replay semantics, and deployment constraints for a gateway or agent-facing application.
For the request choreography, tuning guidance, and gateway-side runbook, see the core flow guide and the protected route contract.
Use Cases
- Public API gating via
purpose=api_gateand route-specificresourcebindings. - Signup, comment, or message throttling via
purpose=rate_limit. - AI inference admission via
purpose=ai_inference_gateplus route, tier, or model metadata inresource. - Agent-native anti-abuse where requesters must solve fresh work before a costly action is admitted.
getmatmulservicechallenge
Issues a domain-bound service challenge. Most integrations pass the first seven arguments and accept the
default fixed policy. The full 13-argument form is for gateways that need adaptive-window
tuning, explicit min/max clamping, or solver-capacity assumptions.
# Common shorthand: first seven arguments, fixed policy defaults
btx-cli getmatmulservicechallenge \
"ai_inference_gate" \
"model:gpt-x|route:/v1/generate|tier:free" \
"tenant:abc123" \
2 300 0.25 0.75 curl --silent --user "$BTX_RPC_USER:$BTX_RPC_PASSWORD" \
--data-binary '{
"jsonrpc":"1.0",
"id":"issue",
"method":"getmatmulservicechallenge",
"params":[
"ai_inference_gate",
"model:gpt-x|route:/v1/generate|tier:free",
"tenant:abc123",
2,
300,
0.25,
0.75,
"adaptive_window",
24,
0.25,
6,
4,
35
]
}' \
-H 'content-type: text/plain;' \
http://127.0.0.1:19334/ Parameters
| Pos | Name | Type | Default | Description |
|---|---|---|---|---|
| 1 | purpose | string | Required | Application policy label such as `rate_limit`, `api_gate`, or `ai_inference_gate`. |
| 2 | resource | string | Required | Bound resource identifier such as `signup:/v1/messages` or `POST:/v1/generate`. |
| 3 | subject | string | Required | Bound requester identifier such as `user:[email protected]`, `tenant:abc123`, or `api_key:key-42`. |
| 4 | target_solve_time_s | number | `1` | Requested service solve-time target in seconds before policy resolution. |
| 5 | expires_in_s | number | `300` | Challenge lifetime in seconds. Must stay between `1` and `86400`. |
| 6 | validation_overhead_s | number | `0` | Estimated validation overhead to include in the total request budget. |
| 7 | propagation_overhead_s | number | `0` | Estimated routing or propagation overhead to include in the total request budget. |
| 8 | difficulty_policy | string | `fixed` | Difficulty policy: `fixed` or `adaptive_window`. |
| 9 | difficulty_window_blocks | number | `24` | Recent block window used when `difficulty_policy=adaptive_window`. |
| 10 | min_solve_time_s | number | `0.25` | Minimum solve-time target allowed after adaptive resolution. |
| 11 | max_solve_time_s | number | `30` | Maximum solve-time target allowed after adaptive resolution. |
| 12 | solver_parallelism | number | `1` | Expected independent solver workers on the client or gateway tier. |
| 13 | solver_duty_cycle_pct | number | `100` | Expected share of wall-clock time those workers may use. |
The node rejects invalid issue requests early. Examples from the live RPC implementation include
expires_in_s must be between 1 and 86400,validation_overhead_s must be non-negative, andsolver_duty_cycle_pct must be greater than 0 and less than or equal to 100.
Response Envelope
| Field | What it gives you |
|---|---|
| `kind` | Envelope version. Current value is `matmul_service_challenge_v1`. |
| `challenge_id` | Replay-resistant identifier for this issued challenge. |
| `issued_at` / `expires_at` / `expires_in_s` | Unix timestamps and lifetime window for the challenge. |
| `binding` | Application binding plus anchor metadata such as `purpose`, `resource`, `subject`, hashes, salt, `anchor_height`, and `anchor_hash`. |
| `proof_policy` | Verification policy and replay contract, including `redeem_rpc`, `solve_rpc`, `issued_challenge_store`, and `issued_challenge_scope`. |
| `challenge` | MatMul challenge payload, including `header_context`, `bits`, `target`, and `service_profile`. |
Key binding fields
| Field | Meaning |
|---|---|
| `purpose` / `resource` / `subject` | The caller-supplied application binding. |
| `resource_hash` / `subject_hash` / `salt` | Hashes and entropy used when deriving the challenge identity. |
| `anchor_height` / `anchor_hash` | The chain tip that anchored issuance time and seeds. |
| `challenge_id_rule` | Documents the exact challenge-id derivation rule used by the node. |
| `seed_derivation_rule` | Documents how per-challenge seeds are derived from `challenge_id` and the anchor. |
Key proof_policy fields
| Field | Meaning |
|---|---|
| `verification_rule` | Current digest / transcript verification contract used by the node. |
| `expiration_enforced` | Whether the node rejects proofs after `expires_at`. |
| `challenge_id_required` | Whether replay-resistant challenge ids are mandatory. |
| `replay_protection` / `redeem_rpc` | Points callers at `redeemmatmulserviceproof` for one-shot admission control. |
| `solve_rpc` | The local helper RPC for daemon-side solving: `solvematmulservicechallenge`. |
| `locally_issued_required` | Whether redeem requires the configured issued-challenge registry to recognize the challenge. |
| `issued_challenge_store` / `issued_challenge_scope` | Whether the registry is local-persistent or shared-file backed for this daemon. |
Key challenge fields
| Field | Meaning |
|---|---|
| `algorithm` | Current algorithm label. Expected value: `matmul`. |
| `header_context` | Chain-anchored header inputs including `previousblockhash`, `seed_a`, and `seed_b`. |
| `bits` / `target` | Difficulty target the client proof must satisfy. |
| `service_profile` | Resolved difficulty, total budget, operator-capacity assumptions, and policy resolution details. |
verifymatmulserviceproof
Stateless verification for one proof envelope. Use it for diagnostics, offline checks, or gateway-side
observability. It does not consume the challenge. Production admission control should still
end at redeemmatmulserviceproof.
curl --silent --user "$BTX_RPC_USER:$BTX_RPC_PASSWORD" \
--data-binary '{
"jsonrpc":"1.0",
"id":"verify",
"method":"verifymatmulserviceproof",
"params":[CHALLENGE_ENVELOPE,"0000000000000001","DIGEST_HEX",true]
}' \
-H 'content-type: text/plain;' \
http://127.0.0.1:19334/ Parameters
| Pos | Name | Type | Default | Description |
|---|---|---|---|---|
| 1 | challenge | object | Required | Challenge envelope returned by getmatmulservicechallenge. |
| 2 | nonce64_hex | hex string | Required | Submitted 64-bit nonce. Must be exactly 16 hex characters. |
| 3 | digest_hex | hex string | Required | Submitted transcript digest. Must be exactly 64 hex characters. |
| 4 | lookup_local_status | boolean | true | When true, also report whether the configured challenge registry knows or has redeemed the envelope. |
Result fields
| Field | Meaning |
|---|---|
| `challenge_id` | Challenge identifier from the submitted envelope. |
| `checked_at` / `expires_at` | Verification timestamp and envelope expiration timestamp. |
| `local_registry_status_checked` | Whether the node consulted its local or shared registry. |
| `issued_by_local_node` | Present when registry lookup is enabled. Shows whether the configured store recognizes the challenge. |
| `redeemed` / `redeemable` / `redeemed_at` | Present when registry lookup is enabled. Shows replay state for this challenge. |
| `valid` / `expired` / `reason` | The decision triple your application should inspect first. |
| `mismatch_field` | Present when canonical challenge verification fails, for example `challenge.bits`. |
| `proof` | Optional diagnostics such as transcript checks, commitment checks, target checks, and parsed nonce / digest values. |
If you set lookup_local_status=false, the registry-backed fields like
issued_by_local_node, redeemed, and redeemable may be omitted. That is
useful for stateless, high-volume verification but it removes replay-state diagnostics.
redeemmatmulserviceproof
Verifies and atomically consumes a locally issued service challenge. This is the anti-replay RPC your application should use before admitting a protected request.
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
The response shape mirrors verifymatmulserviceproof, but the registry-backed replay fields are
always relevant here because redemption is a state-changing operation.
Common reason codes
| Reason | Meaning |
|---|---|
| `ok` | Proof matched the challenge and, for redeem, was accepted and consumed. |
| `invalid_proof` | Nonce and digest failed the canonical MatMul proof checks. |
| `challenge_mismatch` | The submitted envelope was tampered or does not canonically match the anchored challenge payload. |
| `expired` | The challenge lifetime elapsed before verification or redemption. |
| `unknown_challenge` | Redeem could not find the issued challenge in the configured registry, usually because the wrong node handled redemption or the issuing registry is unavailable. |
| `already_redeemed` | Redeem saw a duplicate proof for a challenge that has already been consumed. |
verifymatmulserviceproofs
Batch verification for high-throughput gateways. The request is an array of proof envelopes shaped like
{ challenge, nonce64_hex, digest_hex }. The node enforces a batch size of 1..256.
The response contains top-level counts count, valid, invalid,
per-reason totals in by_reason, and ordered per-proof details in results.
redeemmatmulserviceproofs
Batch verify-and-consume for queue workers and public API frontends. Processing is sequential, not
all-or-nothing, so later duplicates in the same batch can legitimately return already_redeemed.
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/
The batch result includes the same top-level accounting fields as batch verify:
count, valid, invalid, by_reason, and ordered
results.
Related helper RPCs
These helper RPCs are useful when you want to size or simulate challenge budgets for AI gateways rather than manually choosing solve targets.
| RPC | When to use it |
|---|---|
| `solvematmulservicechallenge` | Local helper that solves an issued envelope and returns `nonce64_hex`, `digest_hex`, and a ready-to-submit `proof` object. |
| `getmatmulservicechallengeprofile` | Returns a network-relative difficulty recommendation with ready-to-issue defaults for interactive, balanced, strict, or background flows. |
| `getmatmulservicechallengeplan` | Works backwards from a target throughput objective and returns issuance defaults plus operator-capacity assumptions. |
| `issuematmulservicechallengeprofile` | Issues a challenge directly from a named difficulty profile or short alias. |
Deployment Patterns
Single Node
A single BTX service node is the simplest production shape. Issued challenges are tracked in the node's persistent challenge registry, so restarts do not have to imply a blank in-memory challenge cache.
The current daemon exposes the registry path through -matmulservicechallengefile=<file>.
By default it lives under the network datadir as matmul_service_challenges.dat.
Shared Registry
Multiple BTX service nodes can point at the same -matmulservicechallengefile when you want
issuance on one node and redemption on another. When configured this way, the proof policy exposes a
shared-file-backed issued-challenge store instead of a purely node-local registry.
Multi Node Without Shared State
If nodes do not share a registry, redemption still needs sticky routing or another affinity
layer so the request lands on the issuing node. Otherwise redeem calls can return
unknown_challenge.
Operator rule of thumb: verify can be stateless, but redeem is where your replay guarantees actually live. Design routing, storage, and retries around the redeem path, not around diagnostics-only verification.