Status: Draft
Type: Standards Track
Category: Core
Created: 2025-10-02
Revised: 2026-03-09
Requires: CIP-1 (Actor Scheduler), CIP-3 (Fee Model)
Type: Standards Track
Category: Core
Created: 2025-10-02
Revised: 2026-03-09
Requires: CIP-1 (Actor Scheduler), CIP-3 (Fee Model)
Abstract
This proposal introduces a framework for executing verifiable, off-chain computations within the Cowboy ecosystem. It defines a standardized, asynchronous protocol for smart contracts to request external data fetching, complex computations, or AI model inferences from a decentralized network of off-chain “Runners.” The architecture is built on a deterministic, stake-weighted VRF selection mechanism using Fisher-Yates shuffle, ensuring verifiable task assignment that is resistant to correlation attacks and Sybil manipulation. The core design philosophy emphasizes user-centric verification, task clarity through mandatory result schemas, and on-chain stability via a deferred transaction model for callbacks (per CIP-1).Motivation
To unlock advanced use cases involving AI/ML, large datasets, or Web2 APIs, smart contracts need a secure and reliable bridge to the off-chain world. This CIP proposes a flexible and unopinionated approach, tailored for Cowboy’s on-chain Python environment, that addresses the limitations of existing oracle solutions. This framework empowers developers to:- Integrate Complex Logic: Run Python-based AI model inferences or heavy computations off-chain.
- Preserve On-chain Stability: Utilize an asynchronous, deferred transaction model to prevent network congestion from off-chain interactions.
- Achieve Verifiable Decentralization: Leverage a stake-weighted VRF-based system to eliminate centralized schedulers and allow anyone to verify task assignments.
- Enforce Clarity: Mandate task and result schemas to reduce ambiguity and ensure Runners can reliably execute and be verified.
- Choose Their Trust Model: Allow developers to select the number of Runners, the verification mode, and optional Runner pool constraints for their specific application’s security needs.
Specification
The framework consists of seven on-chain System Actor components and the off-chain Runner network:| Address | System Actor | Role |
|---|---|---|
0x0000…0001 | Runner Registry | Runner registration, staking, capabilities, health, reputation |
0x0000…0002 | Job Dispatcher | Job submission, VRF selection, job lifecycle management |
0x0000…0003 | Result Verifier | Commit-reveal aggregation, result verification, callback dispatch |
0x0000…0004 | Secrets Manager | Encrypted secret storage and TEE-gated secret release |
0x0000…0005 | TEE Verifier | Remote attestation verification for TEE-based runners |
0x0000…0007 | Entitlement Registry | Permission management: Runner Pool access control and general RBAC (see §7) |
| — | Off-chain Runners | External nodes that execute tasks and submit results |
Address gap note: 0x0006 is reserved for future use.
1. Task Definition and Result Schema
Every off-chain task submission must include aresult_schema. This mandatory payload defines the explicit output constraints of the task, enabling objective validation by the protocol.
The schema defines at minimum:
max_return_bytes: Maximum result size to prevent gas-bombing attacks.expected_execution_ms: Target execution time to help Runners assess feasibility.data_format: Expected data structure (e.g., JSON schema, binary format).
2. Job Specification
Every job submitted to the Dispatcher carries aJobSpec:
| Field | Type | Description |
|---|---|---|
job_id | H256 | Unique job identifier |
job_type | JobType | Llm, Http, Mcp, or Custom |
bounds | ResourceBounds | Resource limits for execution |
verification | VerificationConfig | Verification mode and parameters |
max_price | U256 | Maximum price (CBY wei) |
tip | U256 | Priority tip |
timeout_blocks | u64 | Timeout in blocks |
callback | CallbackInfo | Callback Actor and handler |
submitter | Address | Submitting Actor’s address |
submitted_at | u64 | Submission block height |
required_runner_pool | Option<Vec<u8>> | Optional: Pool ID bytes; only Runners holding a RunnerJoinPool(pool_id) Entitlement may be selected (§7) |
3. Core Workflow (Asynchronous & Deferred)
4. Runner Registry & Candidate List Construction
System Actor:0x0000...0001
The Registry maintains runners with full registration data including stake (self‑bonded and delegated per CIP‑13), capabilities, rate card, health, and reputation.
Candidate list construction (at submission_block):
- Health filter:
HealthStatus::Healthy(heartbeat received withinheartbeat_timeout_blocks) - Reputation filter:
reputation ≥ 50 - Capability filter: Runner supports the required
JobType - TEE filter: If
tee_required, runner must declare TEE support (attestation verified by TEE Verifier0x05) - Price filter: Estimated cost ≤
job_spec.max_price - Concurrency filter:
active_jobs < max_concurrent_jobs - Entitlement filter (optional): If
required_runner_pool = Some(pool_id), the Entitlement Registry (0x07) must confirm that the runner holds an Entitlement withScope::RunnerPool(pool_id)andAction::RunnerJoinPool(pool_id)that has not expired and has not exceeded itsmax_uses(see §7.3)
submission_block. Runners who join or leave after submission do not affect the selection for this job.
5. Verifiable Runner Selection (Fisher-Yates VRF)
VRF Seed (deterministic, no private key required):block_hash is the hash of the submission_block, already fixed by consensus. No private key is required. block_hash is a public value — all nodes agree on it — so HMAC’s key-hiding property is unnecessary here. A domain separator achieves the same cross-context isolation.
Implementation note: UseStake-Weighted Fisher-Yates Shuffle (select M from N):cowboy_types::keccak256, which is the project-standard hash primitive (consistent with job ID generation, timer ID generation, token ID generation, etc.). Thepvm_host::randomness()API uses HKDF-SHA256 specifically because it is a security-critical PRF exposed to Actor code; the runner selection seed has no such requirement.
- Selected runners are pairwise independent: no two selections are correlated
- Higher stake → higher probability of selection (Sybil resistance)
- Same
seed+ samecandidatesalways produces the same result (determinism) - Any node can independently verify the selection (verifiability)
Given
(candidates_snapshot, seed, M), re-run the above algorithm to verify that msg.sender is among the selected set. The candidates_snapshot Merkle root is stored at submission time.
6. Timeout-Based Re-selection (replaces skip_task)
The
skip_task interface is removed from this revision. Explicit skipping creates adverse incentives (runners cherry-picking jobs) and unnecessary on-chain transactions. Timeout-based re-selection achieves the same liveness guarantee with better economic alignment.- Selected runners have
timeout_blocksto submit their result commitment. - If no commitment is received within
timeout_blocks:- All timed-out runners receive
reputation -= TIMEOUT_PENALTY(default: 5) - Dispatcher triggers re-selection:
retry_seed = Keccak256(original_seed || "retry:" || retry_count_le4) - Timed-out runners are excluded from the retry candidate list
- A new set of M runners is selected from the remaining candidates
- All timed-out runners receive
- After
MAX_RETRIES(default: 3) consecutive failures, the job transitions toFailedandreputation -= SLASH_THRESHOLDfor persistent non-responders - After
SLASH_THRESHOLDconsecutive slashes, a stake slash is triggered
7. Entitlement Registry (0x0000…0007)
The Entitlement Registry is a system Actor that provides a unified, on-chain permission management layer. For this CIP, its primary role is Runner Pool access control (gating job assignments to a curated set of runners). It also provides general RBAC infrastructure consumed by other subsystems (Token admin delegation, Actor access control, etc.).
7.1 Core Type System
The Entitlement type system is defined incowboy_types::entitlement:
Design note: EachEntitlementrecord covers one(Scope, Action)pair. Granting multiple actions requires multipleGrantcalls (or anAssignRolethat references aRolecollecting them). This keeps revocation granular: revoking one action does not affect others.
7.2 System Instructions (instruction numbers 30–39)
7.3 Runner Pool Membership Model
A Runner Pool is an on-chain access-control list identified by an arbitrarypool_id: Vec<u8>. Membership is represented as an Entitlement:
submission_block):
(grantee, scope, action) exists. used_count is not incremented by the membership check — only by explicit max_uses-bounded grants (e.g. one-time trial membership).
Access modes enabled:
| Mode | required_runner_pool | Effect |
|---|---|---|
| Open (default) | None | Any qualified runner may be selected |
| Whitelist | Some(trusted_pool_id) | Only runners with pool Entitlement |
| Compliance-gated | Some(eu_tee_pool_id) | Only runners with regional or TEE-certified Entitlement |
| Governance-curated | Some(dao_pool_id) | DAO grants pool membership via on-chain governance |
7.4 Gas Costs
| Operation | Cycles | Cells |
|---|---|---|
EntitlementGrant | 5,000 | 500 |
EntitlementRevoke | 2,000 | 100 |
EntitlementDelegate | 3,000 | 300 |
EntitlementCreateRole | 5,000 | 500 |
EntitlementAssignRole / RevokeRole | 2,000 | 100 |
| Entitlement check (per candidate, §4 filter) | 500 | 0 |
7.5 Safety Limits
| Limit | Value | Rationale |
|---|---|---|
| Max Entitlements per address | 256 | Prevent storage inflation |
| Max delegation chain depth | 5 (delegation_depth_max) | Prevent unbounded chains |
| Max Roles per address | 64 | Bound role-expansion attacks |
| Lazy expiry cleanup | On-access | Expired Entitlements deleted on first check post-expiry |
7.6 Backwards Compatibility
- Default pass-through: If the Entitlement Registry Actor does not exist, all checks return
false(no runner is admitted via pool filter). Jobs withrequired_runner_pool = Noneare unaffected. - Existing subsystems: Token
owner == callerchecks remain the first-priority gate; Entitlement provides an additional delegation path, not a replacement. - Instruction numbering isolation: Instructions 30–39 do not conflict with existing instructions 0–29.
8. Result Submission: Commit-Reveal + Designated Aggregator
This revision replaces the original model (all runners independently submit full results to chain) with a commit-reveal + aggregator model that reduces on-chain Gas by ~(N-1)/N and prevents runners from copying each other’s results after seeing them.
- Runners: All M selected runners execute the job independently
- Aggregator: The selected runner with the highest reputation (deterministic, ties broken by address order). Acts as the coordinator.
- Runners cannot copy others’ results after seeing them (commitment pre-locks the result)
- Aggregator cannot forge other runners’ results (commitment-bound)
- Aggregator failure: other runners may submit individual reveals after
aggregator_timeout_blocks; Result Verifier falls back to self-aggregation
9. Verification Modes
| Mode | Runners | Description |
|---|---|---|
| None | 1 | No verification. Dev/test only. |
| EconomicBond | 1 | Single runner with economic stake bond. |
| MajorityVote | N ≥ 3 | Extract vote_field, majority wins with threshold. |
| StructuredMatch | N ≥ 2 | Pipeline of checks: JsonSchema, field matching, numeric tolerance, numeric range, per-field majority. |
| Deterministic | N ≥ 2 | Byte-identical match across all results. Requires tee_required = true for meaningful guarantees (LLM inference is inherently non-deterministic without TEE + fixed model hash). TEE attestation verified by TEE Verifier (0x05). |
| SemanticSimilarity | N ≥ 3 | Cosine similarity clustering; largest cluster meeting threshold wins. |
10. Randomness Evolution Path
The VRF seed source evolves across three phases without changing the selection algorithm or Actor-facing APIs:| Phase | Seed IKM | Security Assumption |
|---|---|---|
| L1 (current) | block_hash of submission_block | Honest supermajority of block proposers |
| L2 (near-term) | block_vrf_output = EC-VRF output by block proposer (in block header) | Honest block proposer per round |
| L3 (long-term) | block_vrf_output = Threshold-BLS t-of-n beacon | t-of-n validators honest |
pvm_host.rs randomness API is designed for zero-Actor-code-change migration: only the IKM source switches internally.
Rationale
- Fisher-Yates VRF over ring-buffer: The original ring-buffer selection (selecting N consecutive indices) is vulnerable to correlation attacks — an adversary controlling adjacent positions in the active list is always selected together for any N-runner job. Fisher-Yates with independent weighted draws eliminates this correlation entirely.
- Stake-weighting: Equal-probability selection ignores stake, making Sybil attacks cheap (register 100 minimal-stake accounts = 100× chance). Logarithmic stake weighting provides proportional incentives without enabling whale monopoly.
-
No private key for VRF seed: Using
Keccak256(block_hash || domain || job_id)instead of a dispatcher private key eliminates both the hardcoded-key security hole and the single-node selection bias.block_hashis consensus-fixed and globally consistent. -
Timeout re-selection over skip_task:
skip_taskcreates adverse incentives (runners selectively skip low-value jobs) and adds unnecessary on-chain transactions. Passive timeout with reputation penalty provides the same liveness guarantee with better economic alignment. - Commit-reveal aggregator: All-runners-submit to chain costs Gas × N and allows result copying. Commit-reveal prevents copying; designating the highest-reputation runner as aggregator is deterministic and Gas-efficient.
- Deferred Transactions (CIP-1): Callbacks are delivered as deferred transactions, decoupling off-chain execution latency from the main chain execution path.
- Mandatory Result Schemas: Creates a clear contract between developers and Runners, prevents gas-bombing, and simplifies result verification.
- Entitlement as membership proof (not just a flag): Pool membership is represented as an on-chain Entitlement record (with expiry, rate-limit, delegatability) rather than a simple boolean flag. This allows time-bounded trial access, revocable membership, and delegation to sub-pools without protocol changes.
-
Single action per Entitlement record: Each record carries one
(Scope, Action)pair. This enables fine-grained revocation without affecting co-granted actions, and keeps the membership check path O(1) per action.
Backwards Compatibility
This CIP is fully backwards compatible with the core protocol. System Actor addresses are unchanged (0x01–0x05, 0x07; 0x06 is reserved). The skip_task interface is removed; existing task submissions that relied on skip behavior will rely on timeout re-selection instead, which provides equivalent liveness guarantees.
Security Considerations
-
VRF Grinding: A submitter could attempt to choose
submitted_atto influence the seed. Sinceseed = Keccak256(block_hash || domain || job_id || submitted_at)andblock_hashis consensus-fixed before the submitter can observe it, this attack requires pre-computing the block hash — equivalent to breaking the consensus security assumption. -
Stake Concentration: The logarithmic weight compression
log2(effective_stake/MIN_STAKE + 1) + 1limits the selection advantage of high-stake runners (including delegated stake per CIP‑13). A runner with 1024× the minimum effective stake gets only 11× the selection weight, not 1024×.MIN_STAKE = 10,000 CBY. - Aggregator Collusion: The Aggregator sees all results before submitting to chain. Mitigations: (1) other runners’ commitments are locked before Aggregator submits; (2) other runners can independently reveal if Aggregator is unresponsive; (3) Aggregator’s extra reward is forfeit if the submitted VerifiedResult is later challenged.
-
Active List Manipulation: Minimum stake (
MIN_STAKE) and reputation threshold (min_reputation = 50) provide economic deterrence. The Entitlement pool mechanism (§7) allows job submitters to further restrict runner eligibility. -
Callback Griefing: A malicious developer could write a callback that always fails, preventing runners from being paid. The
failed_callbackscounter accrues as a reputational penalty, and runners may blacklist actors with high failure rates. -
Historical Snapshot Integrity: The candidate list is fixed at
submission_block. The Merkle root of the candidate list must be stored on-chain at submission time to enable re-selection verification. Runners who join or leave post-submission do not affect the snapshot. -
Entitlement Inflation: Each address is limited to 256 active Entitlements and 64 assigned Roles.
EntitlementGrantconsumes 500 Cells to deter spam. Expired Entitlements are lazily cleaned on first post-expiry check. -
Delegation Chain Depth:
delegation_depth_max(max 5) and the requirement that delegated constraints be a strict subset of parent constraints prevent privilege escalation through chained delegation. -
Pool Membership Expiry: Pool Entitlements with
valid_untilset are automatically ineligible after the specified block. Node operators running compliance-sensitive pools should set time-bounded grants and renew via governance.

