Skip to content

Anatomy of a Myelin CellTx

A Myelin CellTx is the unit of state change. It is structurally a CKB transaction, with extra Myelin-side metadata attached so the scheduler and consensus layers can reason about it deterministically. This page walks through what each field does and why Myelin needs its own copy of some of them.

The shape

pub struct MyelinCellState {
    pub live_cells:     Vec<LiveCell>,
    pub consumed_cells: Vec<OutPoint>,
    pub created_cells:  Vec<CellOutput>,
    pub cell_deps:      Vec<CellDep>,
    pub context_deps:   Vec<ContextDep>,
    pub witnesses:      Vec<Vec<u8>>,
    pub state_root:     [u8; 32],
}

A Myelin CellTx is the transition from state_root_before to state_root_after, with everything above as the witness for the transition. There is no global mutable state outside this Cell set — that is the entire point of staying close to CKB.

The CKB side

These fields are exactly the CKB transaction shape. They live in myelin-exec and are encoded using Molecule-compatible tables:

Field CKB equivalent What it carries
consumed_cells inputs[].previous_output OutPoints referencing live Cells.
created_cells outputs[] New CellOutput { capacity, lock, type, data }.
cell_deps cell_deps[] Code Cells or dep-group Cells.
witnesses witnesses[] Signatures, arguments, off-chain data slots.

[!NOTE] Myelin does not reuse the CKB client's wire format — it uses the same shape, the same semantics, and the same Molecule encoding so a projection layer can wrap it as a CKB tx.

The Myelin side

These fields are what make a CellTx admittable on Myelin without trusting the producer:

Field Why Myelin needs it
context_deps Session-scoped dependencies that aren't necessarily CKB cell_deps.
state_root The local 32-byte commitment used to anchor the transition into a block.
semantic_profile ckb-compatible / myelin-native / ckb-inspired-only — drives projection decisions.
typed_data_hash Hash of CellOutput.data under the declared type schema.
conflict_hash Hash of (read_set, write_set, conflict_domains).
scheduler_witness Canonical witness over the CellTx + metadata; required for admission.
proof_obligations Things the verifier must check on replay (cycle limits, dep presence, etc.).

The scheduler rejects CellTxs whose typed_data_hash doesn't match the actual output data, whose conflict_hash doesn't cover the declared read/write sets, or whose scheduler_witness is malformed. See CellDAG scheduler for the rejection taxonomy.

Projection — what it actually does

A projection takes a Myelin CellTx and answers: "Could this be represented as a CKB transaction without changing semantics?" The output is a CkbProjectionReport:

pub struct CkbProjectionReport {
    pub projection_possible: bool,
    pub ckb_style_tx_hash:   Option<[u8; 32]>,
    pub cell_inputs:         Vec<OutPoint>,
    pub cell_outputs:        Vec<CellOutput>,
    pub cell_deps:           Vec<CellDep>,
    pub witnesses:           Vec<Vec<u8>>,
    pub script_groups:       Vec<ScriptGroup>,
    pub unsupported_features:          Vec<String>,
    pub semantic_deviation_flags:     Vec<String>,
}

projection_possible: true means every consumed Cell, produced Cell, dep, witness, script group, and VM syscall in the Myelin CellTx can be encoded in a CKB-style context without losing semantics. If it's false, the report explicitly lists what couldn't be projected.

%%{init: {
  "theme": "base",
  "themeVariables": {
    "primaryColor": "#A5B4FC",
    "primaryTextColor": "#1E293B",
    "primaryBorderColor": "#4F46E5",
    "lineColor": "#6366F1",
    "secondaryColor": "#C7D2FE",
    "tertiaryColor": "#C7D2FE"
  }
}}%%
flowchart TB
    subgraph MY["Myelin CellTx"]
        M1["consumed_cells"]:::m
        M2["created_cells"]:::m
        M3["cell_deps"]:::m
        M4["witnesses"]:::m
        M5["typed_data_hash"]:::m
        M6["conflict_hash"]:::m
        M7["scheduler_witness"]:::m
    end
    subgraph PROJ["CkbProjectionReport"]
        P1["ckb_style_tx_hash"]:::p
        P2["cell_inputs"]:::p
        P3["cell_outputs"]:::p
        P4["cell_deps"]:::p
        P5["witnesses"]:::p
        P6["script_groups"]:::p
        P7["unsupported_features"]:::p
        P8["semantic_deviation_flags"]:::p
    end
    subgraph CKB["CKB-style tx (Molecule)"]
        K1["inputs[]"]:::k
        K2["outputs[]"]:::k
        K3["cell_deps[]"]:::k
        K4["witnesses[]"]:::k
    end
    M1 --> P2 --> K1
    M2 --> P3 --> K2
    M3 --> P4 --> K3
    M4 --> P5 --> K4
    M5 & M6 & M7 --> P7
    M5 & M6 & M7 --> P8
    P2 & P3 & P4 & P5 --> P1
    classDef m fill:#C7D2FE,stroke:#6366F1,color:#1E293B;
    classDef p fill:#C7D2FE,stroke:#4F46E5,color:#1E293B;
    classDef k fill:#A5B4FC,stroke:#7C3AED,color:#1E293B;

The projection layer is the credibility hinge: every serious demo should pair the execution report with the projection report, so the reader can see whether the CellTx is genuinely CKB-projectable or explicitly carries deviation flags.

Why both layers exist

If Myelin only produced execution reports, every claim about "CKB-alignment" would be hand-waved. If Myelin only produced projection reports, there'd be nothing to run off-chain. Keeping both means:

  • the execution report answers "did the VM accept this?"
  • the projection report answers "can this be replayed on CKB without changing meaning?"

The two together produce the evidence Myelin's claim ladder is built on. See Claim ladder.

What's not in a CellTx

A CellTx is deliberately small. It does not contain:

  • The full Cell set — only the consumed and created Cells.
  • Block headers — only the state root, which is enough to anchor it into a block.
  • Validator signatures — those go on the block certificate, not on the individual CellTx.
  • DA chunks — those go on the DA manifest, addressed by the chunk payload hash.

This keeps the CellTx itself small enough to fit inside a court bundle and verifiable in a CKB-VM-style court verifier without breaking the VM cycle budget.