TASK_SUBMIT, TASK_RESPONSE, TASK_PROGRESS, TASK_VALIDATE, TASK_REWARD
The five-stage AI task pipeline.
USER MINER VALIDATOR CHAIN
[TASK_SUBMIT] ─────► (poll) ──► run LLM ──► [TASK_RESPONSE] ─────► validate ──► [TASK_VALIDATE]
│
▼
[TASK_REWARD]
A miner may also emit TASK_PROGRESS mid-inference for long jobs (informational only).
TASK_SUBMIT
| Field | Type | Notes |
|---|---|---|
type | "TASK_SUBMIT" | — |
from | string | Submitter |
taskId | string | Caller-generated unique ID (typically 32 hex from randomBytes(16)) |
taskType | string | One of: chat, code_simple, code_agentic, image, audio, video, embedding, nft_svg, challenge |
prompt | string | User input |
fee | string (bigint) | Micro-OMBRA, must be >= MIN_TASK_FEE = 10_000 |
timeoutMs | number | Auto-computed from TASK_TIMEOUTS[taskType] |
nonce, timestamp | — | — |
Validation:
taskTypeis a known valuefee >= MIN_TASK_FEEstate.balance(from) >= feetaskIdnot already used in state
Effect:
state.balance(from) -= fee(escrow)state.tasks[taskId] = { status: "submitted", submitter: from, fee, prompt, taskType, timeoutMs, ... }
TASK_RESPONSE
| Field | Type | Notes |
|---|---|---|
type | "TASK_RESPONSE" | — |
from | string | Miner wallet |
taskId | string | Refers to existing TASK_SUBMIT |
minerId | string | Miner NFT id (sha256 of MINER_REGISTER hash) |
contentHash | string | sha256(content) — for verification |
content | string | Inference output |
thinking? | string | Optional chain-of-thought (for reasoning models) |
model? | string | Model identifier (e.g., "llama-server:Qwen2.5-7B") |
tokensIn? | number | Input tokens (informational) |
tokensOut? | number | Output tokens (used for PoATU reward) |
thinkingTokens? | number | Reasoning tokens (used for PoATU) |
durationMs? | number | Inference duration |
Validation:
- Referenced task exists and
status == "submitted" - Submitter is a registered miner (
MINER_REGISTERtx exists forfrom) contentHash == sha256(content)- Tx submitted within
submittedAt + timeoutMs
Effect:
- Append to
state.taskResponses[taskId] - If first response, set
state.tasks[taskId].firstResponseAt
TASK_PROGRESS
| Field | Type | Notes |
|---|---|---|
type | "TASK_PROGRESS" | — |
from | string | Miner wallet |
taskId | string | — |
minerId | string | — |
message | string | Free-form progress note |
Effect: Append to state.taskProgress[taskId]. Pure informational, no balance changes.
TASK_VALIDATE
| Field | Type | Notes |
|---|---|---|
type | "TASK_VALIDATE" | — |
from | string | Validator (registered miner with judge capability) |
taskId | string | — |
validatorAddress | string | Same as from |
scores | array | [{ minerId, score (0-100), reasoning }] |
bestMinerId | string | The winning response's minerId |
validatorModel? | string | Judge model used |
judgeRawOutput? | string | Raw LLM scoring response |
Effect:
state.tasks[taskId].status = "validated"- Stores
bestMinerId,scoresfor downstreamTASK_REWARD
TASK_REWARD
| Field | Type | Notes |
|---|---|---|
type | "TASK_REWARD" | — |
from | string | Validator (issuing reward) |
taskId | string | — |
rewards | array | [{ address, amount (string bigint), reason }] where reason ∈ {"miner", "validator", "burn"} |
Validation:
- Task is in
"validated"status - Sum of
rewardsmatchestask.fee(modulo rounding) - Reward distribution matches
FEE_SPLIT_V2(70/25/5)
Effect:
- For each reward: credit address, or burn (no recipient)
state.tasks[taskId].status = "rewarded"
Builders
sdk.tasks.submitTask({ taskType, prompt, fee, taskId? }) // high-level: build+sign+broadcast
buildTaskSubmitTx(from, taskId, taskType, prompt, fee, nonce, privKey)
buildTaskResponseTx(from, taskId, minerId, content, nonce, privKey, opts)
buildTaskProgressTx(from, taskId, minerId, message, nonce, privKey)
buildTaskValidateTx(from, taskId, validatorAddress, scores, bestMinerId, nonce, privKey, opts)
buildTaskRewardTx(from, taskId, rewards, nonce, privKey)