Skip to main content
Status: Draft Type: Standards Track Category: SDK

Overview

This improvement proposal aims to hide underlying mechanisms (message passing, Timer, Gas) through high-level abstractions while strictly adhering to the PVM determinism constraints defined by the Cowboy mainchain (no JIT, soft-float, fixed hash seed, CBOR serialization).

Design Principles

  1. Determinism First: All SDK abstractions must compile to deterministic on-chain operations
  2. Explicit Over Implicit: State crossing block boundaries must be explicitly declared
  3. Secure by Default: Prevent developers from inadvertently writing code that breaks consensus
  4. Progressive Complexity: Simple APIs for simple scenarios, full control for complex scenarios

Chapter 1: Call Primitives and Delivery Timing

1.1 Three Call Primitives

The Cowboy SDK provides three call primitives for different scenarios:

1.2 Execution Sequence Diagram

1.3 Synchronous Call (call) - T+0

Synchronous calls execute immediately within the current transaction, sharing atomic context. PVM Determinism Constraints:
  • Call depth accumulates to reentrancy limit (32), each call() consumes 1 depth level
  • Must explicitly pass cycles_limit to prevent infinite recursion
  • Return values must be CBOR serializable types
Syntactic Sugar: ActorRef

1.4 Asynchronous Message (send) - T+N

Asynchronous messages are queued for delivery in the next block, with no return value and irrevocable. PVM Determinism Constraints:
  • Multiple send() calls within the same transaction queue messages in call order
  • Message ID: keccak256(sender_addr + nonce + target + payload_hash)
  • Messages are strictly delivered at the start of the next block
⚠️ Fire-and-Forget Risks and Compensation Patterns

1.5 Reentrancy and Circular Calls


Chapter 2: Continuation Mechanism

Continuation is the core mechanism for Cowboy to handle cross-block asynchronous operations. The SDK provides two Continuation decorators: Both share the same compilation strategy and state machine mechanism, differing only in the await target.

2.1 Compilation Strategy: Explicit State Machine Transformation

The SDK compiles async functions into Finite State Automata (FSM), with each await point defining a state. PVM Determinism Constraints:
  • State serialization uses Canonical CBOR
  • State ID: keccak256(actor_addr + method_name + invocation_nonce)
  • Each Continuation state occupies Actor storage quota
  • Captured variables must be CBOR serializable types (closures, function references, generators are prohibited)

2.2 capture() - Explicit State Capture

Developers must use capture() to explicitly declare variables that need to be preserved across await:

2.3 Supported Patterns and Limitations

2.4 Conditional Branch await

2.5 Bounded Loop await

Using await in loops requires declaring iteration upper bound:

2.6 Error Handling with await

2.7 @actor.continuation - Inter-Actor Async Calls

For async request-response patterns between Actors (not Runner):

2.8 Continuation State Storage

2.9 Compilation Output Example (Informative)

Original code:
Compiled equivalent:

Chapter 3: State Safety Mechanisms

Cowboy provides two complementary state safety mechanisms:

3.1 Guard Mechanism - State Protection

Guard prevents stale state vulnerabilities caused by cross-block execution. PVM Determinism Constraints:
  • Object identity comparison (id() or is) is prohibited
  • State fingerprint uses Canonical CBOR + keccak256
  • Cannot use pickle or unstable JSON

Method A: Decorator-Level Guard

Method B: Object-Level Fine-Grained Guard

3.2 Collaboration Between Guard and Capture

guard and capture solve different problems and can be used together:
Summary of Differences:
  • capture() saves local variables (temporary values within the function)
  • guard_unchanged verifies storage state (Actor’s persistent data)

Chapter 4: Async Tools

4.1 Timeout and Retry

PVM Determinism Constraints:
  • Time units must be block height, using seconds or time.time() is prohibited
  • Retry jitter must use on-chain VRF, random.random() is prohibited
SDK Internal Implementation:
  • Timer ID generation: keccak256(current_msg_id + "timer"), ensures consistency across all nodes
  • Automatic cleanup: When Runner result is received or Timeout triggers, SDK automatically cancels the other resource

4.2 TaskGroup - Structured Concurrency

Allows developers to write parallel tasks with synchronous code thinking. PVM Determinism Constraints:
  • Task creation order within TaskGroup must be strictly consistent, determining message Nonce and hash
  • When aggregating results, SDK returns results in deterministic order (by task creation order)

Chapter 5: Type System

5.1 CowboyModel - PVM-Safe Data Model

Standard Pydantic BaseModel may use non-deterministic behavior. The SDK provides a customized CowboyModel: PVM Determinism Constraints:
  • Python native float depends on hardware FPU, is non-deterministic
  • Must use SoftFloat instead of float
  • Decimal must specify precision

5.2 PVM-Specific Types


Chapter 6: Declarative Verification Builder

Use fluent chaining to replace hand-written complex verification JSON configuration. PVM Determinism Constraints:
  • Regardless of how code is called, the final generated Job Spec JSON must have ordered keys

6.1 Basic API

6.2 Verification Modes

6.3 Built-in Checkers

6.4 Complete Example


Chapter 7: Mixed Usage Patterns

7.1 Comprehensive Example


Appendix A: PVM Compatibility Iron Rules

Developers must adhere to the following rules:

Appendix B: Call Semantics Quick Reference

B.1 Call Primitive Selection


Appendix C: Mechanism Comparison Table