CellFabric (cell-fabric) is an independent Rust crate for a CKB-settled cell-intent layer.
It is intentionally not a service binary: gateway, orderer, submitter, and proof
services should build on this deterministic domain layer.
For a protocol principles and tutorial walkthrough, see docs/principles-tutorial.md. For implementation red lines, see docs/red-lines.md. For the Chinese README, see README.zh.md.
CellFabric models an upper-layer cell-intent DAG with deterministic compilation into CKB settlement transactions.
The crate assumes:
- CKB L1 owns finality.
- the upper layer may order, aggregate, replace, and soft-confirm intents.
- every economically meaningful transition must ultimately be represented as concrete CKB inputs, outputs, witnesses, and deps.
- double-spend safety comes from explicit conflict domains, not from hiding or dropping conflicting intents.
- settlement compilation is a pure transformation over immutable bundle snapshots, not a service callback into live mutable state.
The core loop is:
- admit signed cell-native intents.
- index consumed cells, app conflict keys, dependencies, and replacements.
- quarantine visible conflicts without erasing evidence.
- select only conflict-free bundles.
- issue explicitly non-final soft confirmations.
- compile selected bundles into concrete CKB transactions.
- submit transactions to CKB and track L1 status.
- record
Settledonly after CKB commitment/finality, orRejectedif L1 explicitly rejects settlement material.
The crate is built around these failure modes:
- local and cross-orderer double-spend propagation.
- orderer equivocation, selective ordering, and local censorship.
- app-level conflict ambiguity in AMM, launch, matching, or shared-state namespaces.
- nondeterministic bundle construction or settlement compilation.
- off-chain reentrancy-style corruption from compiling against live mutable DAG state.
- confusing propagation, submission, or soft confirmation with L1 finality.
- proof formats that are useful off-chain but not yet enforceable on CKB.
The intended safety posture is conservative: conflicts remain visible, soft confirmation remains non-final, and any final settlement claim must trace back to CKB transaction commitment.
Obyte is useful as a structural reference, not as this protocol's identity. The useful intuition is block-free fast-path organization: upper-layer activity can be represented as a graph of transactions or intents rather than as mini-blocks.
CellFabric deliberately takes only that structural intuition:
- the fast path is graph-native and intent-centric.
- ordering actors help produce usable order, but do not own finality.
- graph observability should expose frontier, lineage, conflicts, and confidence without hiding that settlement is still pending.
The crate must not inherit Obyte's standalone DAG-ledger worldview:
- CKB remains the final settlement and verification anchor.
- soft confirmation must never become social or de facto finality.
- orderers are not consensus actors.
- generic DAG elegance must not override CKB cell discipline, explicit conflict keys, app-specific compiler boundaries, or proof-to-script closure.
In short: borrow the block-free transaction-fabric intuition; do not turn CellFabric into a separate DAG ledger whose final truth lives above CKB.
- no finality without CKB settlement.
- no valid bundle may contain direct consumed-cell conflicts or declared app-level conflicts.
- no namespace is safely settleable unless its conflict policy and settlement compiler are deterministic and bounded by signed intent resources.
- no settlement compilation may depend on mutable live engine state.
- no app module may observe partially applied intra-bundle post-state unless that ordering is explicitly represented by dependencies.
- no orderer censorship assumption may be treated as permanent; timeout exit or direct proof paths must remain part of the protocol roadmap.
Double-spends are not eliminated by the DAG. They are made explicit and excluded from valid settlement.
The required flow is:
- Admit: accept the conflicting intent if its structure and auth binding are valid.
- Quarantine: record
ConflictRecordentries by consumedOutPointRefand declaredAppConflictKey. - Exclude: bundle selection must never co-select conflicting intents.
- Evidence: proof service must be able to reconstruct the signed intents, conflict key, bundle lineage, and settlement lineage.
- Invalidate / Exit: future CKB scripts must turn that evidence into dispute, invalidation, or user-exit paths.
Admission-time rejection alone is not enough. A decentralized path requires conflict transparency so users and independent orderers can audit censorship, equivocation, and hidden replacement behavior.
This is not EVM-style contract reentrancy, but upper-layer services can still corrupt state if ordering, app preview, and settlement compilation share mutable state.
The rule is strict: settlement compilation and app preview must be pure over an
immutable pre-state snapshot. They must not read from a live IntentDag, mutate
the engine, perform callbacks into app services, or depend on partially applied
intra-bundle writes.
Mutation-heavy structures belong behind the single-writer actor. External RPC, DB, CKB node calls, and tx submission must happen outside mutable engine access.
The current crate is deliberately a single-writer deterministic core. That is a correct Phase 0/1 tradeoff: correctness, reproducibility, and testability come before open ordering.
The protocol path is:
- single orderer / devnet services over this deterministic core.
- multiple known orderers independently building and signing reproducible bundles.
- open orderer admission only after auth, data availability, exit, and dispute paths are enforceable.
The long-term assumption is not permanent trust in one operator. Users must retain a path to audit conflicts, bypass censorship through timeout exits, and challenge invalid or inconsistent settlement evidence.
The crate implements the Phase 0/1 core needed before AMM or launchpad logic:
- canonical intent and bundle hashing
- CKB-native cell references, script specs, and output templates
- DAG insertion, causal/replacement edges, and conflict indexes
- app conflict policy registry for deterministic app-level conflict keys
- pluggable auth verifier registry plus a CKB secp256k1-blake160 recoverable
signature verifier for
ckb-lockintents - deterministic conflict-free bundle selection
- soft-confirmation receipts that are explicitly non-final
- bundle data-availability manifests whose roots commit to ordered intent bodies, auth material, and excluded conflict records
- secp256k1-signed bundle receipts plus verifier registry for auditable orderer soft-confirmation evidence
- receipt quorum validation for multi-orderer non-final evidence
- service-facing gateway request/response types over the single-writer actor
- orderer-facing bundle build and soft-confirmation adapter with service-local app policy / auth verifier enforcement
- event-sourced status tracking for intent lifecycle transitions
- proof skeletons for double-consume, app-conflict, and replacement disputes
- proof-service adapter for conflict proofs, dispute proofs, and timeout exits
- direct CKB transaction draft and settlement-plan construction for mint/transfer
- settlement plan hashes and evidence commitments binding concrete CKB tx bytes to bundle data-availability roots
- direct compiler enforcement for intent-local output constraints
- app conflict policy plus settlement compiler enforcement for namespace-specific
Swap/Appactions - submitter abstraction with dry-run lifecycle recording
- settlement recovery policy for retry / recompile / abandon decisions after submitter failures or rejected L1 observations
- fee-bump planner that derives deterministic retry/recompile fee intent from signed bundle fee bids and caps without mutating signed intents
- fee-bump plan compiler hook that requires an explicit service executor before higher-fee settlement material can be produced
- settlement worker that scans due recovery statuses, recompiles immutable bundles, and resubmits through the same submission service
- fee-bump-aware worker path that only activates when the caller provides both a planner and an explicit fee-bumped plan compiler
- tx-pool eviction detector / monitor trait that converts node or indexer eviction signals into explicit recovery actions
- optional
httpfeature exposing Axum routes for gateway/orderer/proof/submitter APIs - optional
ckb-rpc-submitfeature exposing a JSON-RPCsend_transactionsubmitter - L1 transaction status tracking with optional confirmation-depth finality policy and rejected-terminal recording
- settlement finality evidence that binds plan evidence to committed/finalized CKB transaction observations
- a Tokio actor handle for single-writer mutation of the in-memory engine
SignedIntent::idis derived only fromIntentBody, not from auth bytes.- all canonical hashed structures use stable
BTreeMap/BTreeSetordering. - hard conflicts are keyed by consumed
OutPointRef. - app conflicts are explicit
AppConflictKeyvalues insideResourceAccess. - registered app conflict policies require declared app keys to match deterministic policy output.
- gateway auth verifier registry can reject intents before actor submission.
- orderer config can reject invalid app policies or auth before bundle storage and soft confirmation.
- gateway and orderer configs expose production guards that reject permissive, empty, or noop auth verifier registries.
- selectors never include two selected intents with the same consumed cell or app key.
- selectors reject missing dependencies instead of silently treating them as optional.
- selectors reject active intents from a different target chain.
- bundle validation rejects duplicate intents, internal conflicts, dependency-order violations, and superseded/replacement co-selection.
- settlement compilation operates on immutable
Bundlesnapshots. - direct settlement compilation enforces
MinOutputCapacity,ExactOutputLock, and declaredMaxFeeconstraints for supported actions. Swapand genericAppintents are not safely settleable unless the namespace has both an app conflict policy and an app settlement compiler.- soft confirmations reduce to
IntentStatus::SoftConfirmed { non_final: true, .. }and are never represented as final settlement. BundleReceipt.data_availability_rootcommits to the bundle evidence manifest, including auth bytes; changing auth material preserves intent semantic identity but invalidates the signed receipt for that evidence set.- signed bundle receipts prove which registered orderer key signed a non-final receipt, but they are still not L1 finality or settlement proof.
- receipt quorum validation counts distinct registered orderer signatures over the same bundle and data-availability root; it strengthens evidence, not finality.
- settlement plan tx hash hints are verified against raw CKB transaction bytes before compiled/submitted statuses are recorded.
SettlementPlanEvidencebinds bundle id, data-availability root, plan hash, and concrete tx hashes for audit and dispute handoff.SettlementFinalityEvidencebinds settlement plan evidence to per-tx committed block anchors, and finalized evidence additionally records tip height and confirmation-depth policy.- settlement observation recording treats L1
Rejectedas a terminal bundle rejection before considering committed/finalized settlement. - retry / recompile / abandon outcomes are explicit ledger statuses; submitter failures must not disappear as untracked service errors.
- fee-bump planning is advisory and snapshot-based; it must not rewrite
IntentBody, ignore signed max-fee caps, or pretend a higher bid is already reflected in existing transaction bytes. - fee-bumped recovery compilation must use an explicit
FeeBumpPlanCompiler; the default rejecting compiler fails closed instead of silently resubmitting unchanged transaction bytes as if fees were increased. - settlement workers read bundle snapshots through the actor and compile outside engine mutation; they do not access a live mutable DAG.
- tx-pool eviction monitors only record policy-derived recovery actions; they do not infer finality or mutate bundle contents.
Any implementation agent extending this crate should read
docs/red-lines.md before changing protocol-facing
behavior. The red lines are treated as merge blockers, not advisory notes.
types: core CKB-facing domain types and canonical encodersavailability: bundle evidence manifest and data-availability rootsdag: intent graph, replacement edges, conflict indexes, snapshotsbundle: deterministic selector, bundle IDs, soft-confirmation receiptsvalidation: intent, bundle, unsigned receipt, and signed receipt validationpolicy: app conflict policies, policy registry, replacement validationauth: pluggable auth verifier registry and auth kind routingledger: event log and reduced user-visible status stateengine: synchronous core engine over anIntentStoreactor: Tokio single-writer command boundary aroundIntentEnginegateway: ingress/query API trait and actor-backed adapterorderer: bundle selection and non-final soft-confirmation adapter; validated orderers can inject app policies and auth verifiers before soft confirmationreceipt_auth: secp256k1 bundle receipt signing and verifier registry for non-final orderer evidence, plus receipt quorum policyproof_service: actor-backed exit/dispute evidence constructiondraft: direct CKB transaction draft builder for simple actionscompiler: reusable settlement compiler trait and direct compilerapp_compiler: app-specific settlement compiler registry and output fragmentssettlement: serialized CKB tx bytes, settlement plans, plan hashes, and plan evidence commitmentsrecovery: settlement retry, recompile, and abandon policy decisionsfee_bump: deterministic bundle fee snapshotting, retry/recompile fee-bump planning, and explicit fee-bumped settlement-plan compiler hookssubmitter: CKB submission trait, dry-run submitter, lifecycle recorder, and recovery-aware submission serviceworker: due settlement recovery scanner and bounded retry/recompile executortracker: L1 tx status provider trait, settlement observation recorder, and bundle settlement/finality evidence buildertx_pool: tx-pool eviction detector trait and recovery monitorrpc_submitter: optional CKB JSON-RPCsend_transactionadapter behind--features ckb-rpc-submit; it also implementsL1TxStatusProviderthroughget_transactionhttp: optional Axum router and handlers behind--features httpevidence: exit/dispute proof skeletonsstore: store abstraction, memory store, atomic JSON file store, and checksum-validated append-only journal store
This crate does not implement RocksDB/Postgres storage, an external data
availability network, estimate CKB fees, rewrite settlement transactions for
fee bumping, or ship app-specific swap semantics. Those are service-layer
responsibilities. It does include JournalIntentStore, an append-only,
checksum-validated local store that is stronger than the dev JSON snapshot store
and useful for single-node services or integration testing before replacing the
store with RocksDB/Postgres. The fee_bump module only computes explicit
fee-bump intent from immutable bundle snapshots and routes higher-fee material
through FeeBumpPlanCompiler; executors must still estimate fees and recompile
or rebuild concrete CKB transactions before submitting higher-fee material.
Swap and generic App intents require both a registered app conflict policy
and a registered app settlement compiler for their namespace. The direct
compiler enforces this boundary: missing policy returns MissingAppConflictPolicy,
and missing compiler after policy validation returns MissingAppSettlementCompiler.
The auth layer provides a verifier trait and registry, and includes
CkbSecp256k1Blake160Verifier for CKB-style recoverable secp256k1 signatures.
That verifier is intentionally strict: because IntentAuth::CkbLockSignature
contains only a lock script hash, production services must supply a trusted
mapping from lock script hash to expected secp256k1-blake160 lock arg, usually
from wallet context, an indexer, or resolved live cells. Other auth modes such
as ckb-auth, multisig, hardware wallets, or escrow auth should register their
own AuthVerifier implementations.
Before starting production ingress or ordering services, call
GatewayConfig::assert_production_ready and
OrdererConfig::assert_production_ready; the default configs are intentionally
dev-friendly and will fail those checks.
The optional CkbRpcSubmitter calls CKB JSON-RPC send_transaction and records
the returned transaction hashes. This is only L1 submission, not finality:
CKB may still reject or later fail to commit a submitted transaction, so a
production service must additionally track tx-pool and chain status.
SettlementTracker provides that handoff: it observes tx status through an
L1TxStatusProvider and can either wait, record Settled, or record Rejected
through record_terminal_if_committed. For production-like flows, use
ConfirmationDepth with record_terminal_if_finalized; the optional RPC
submitter implements both L1TxStatusProvider through get_transaction and
L1TipProvider through get_tip_block_number. Dev and test services that
already have an L1 observation can use SettlementObservationRecorder directly
without binding their HTTP surface to a concrete RPC client.
SettlementRecoveryPolicy provides the domain state machine for retry,
recompile, or abandon decisions. It records those outcomes through the same
single-writer actor. FeeBumpPolicy can attach a deterministic fee-bump
decision to those recovery actions using the highest bundle fee bid and the
lowest signed max-fee cap. compile_recovery_plan_with_fee_bump then routes
Keep decisions through the ordinary compiler and routes Bump /
RequiresRecompile decisions through an explicit FeeBumpPlanCompiler.
RejectingFeeBumpPlanCompiler is the fail-closed default for services that have
not implemented real fee estimation or transaction rebuilding.
SettlementWorker is the minimal executor for those recorded actions. It scans
the reduced ledger for due SettlementRetryScheduled and
SettlementRecompileRequested statuses, fetches the stored immutable bundle,
compiles a fresh plan, and resubmits it through SettlementSubmissionService.
The default run_due_once path does not rewrite fee parameters. Services that
want fee-bumped execution must call run_due_once_with_fee_bump and provide
both a FeeBumpPlanner and a FeeBumpPlanCompiler. Fee cap exhaustion or a
missing fee-bump executor fails closed into explicit recovery state instead of
silently reusing old transaction bytes.
The worker still does not detect tx-pool eviction by itself; those signals should be fed into the recovery state machine by a real node/indexer integration and, when needed, the explicit fee-bump-aware worker path.
TxPoolEvictionDetector and TxPoolEvictionMonitor provide that integration
point. A detector reports that submitted tx hashes have been evicted or have
otherwise become non-viable according to node/indexer policy; the monitor turns
that signal into a SettlementRecoveryPolicy::after_tx_pool_eviction action and
records it through the actor.
TxStatusEvictionDetector is the default status-based detector: it queries an
L1TxStatusProvider such as CkbRpcSubmitter and treats configured
Unknown / Pending / Proposed timeouts as tx-pool eviction signals. Those
signals schedule recovery only; they are not settlement finality.
Submitted timestamps are recorded in IntentStatus::L1Submitted, so services
can derive active TxPoolSubmittedCheck values from the reduced ledger via
active_tx_pool_submitted_checks or let TxPoolEvictionMonitor scan active
submissions directly.
Run the full core tests:
cargo test -p cell-fabricEnable the Axum dev server:
cargo test -p cell-fabric --features http
cargo run -p cell-fabric --features http --bin cell-fabric-httpEnable the CKB RPC submitter:
cargo test -p cell-fabric --features ckb-rpc-submitEnable both integration surfaces:
cargo test -p cell-fabric --features "http ckb-rpc-submit"The http feature exposes an Axum router and a small in-memory dev server:
cargo run -p cell-fabric --features http --bin cell-fabric-httpEnvironment variables:
CELL_FABRIC_HTTP_BIND, default127.0.0.1:8118CELL_FABRIC_CHAIN_ID, defaultckb-devCELL_FABRIC_ACTOR_BUFFER, default1024CELL_FABRIC_STORE_PATH, optional file/journal store pathCELL_FABRIC_STORE_KIND,fileorjournal, defaultfileCELL_FABRIC_JOURNAL_SYNC, defaulttruewhenCELL_FABRIC_STORE_KIND=journal
The server is intentionally a dev surface. It uses MemoryIntentStore and
DryRunCkbSubmitter by default. Set CELL_FABRIC_STORE_PATH to persist intents,
bundles, and events. Use CELL_FABRIC_STORE_KIND=file for an atomic JSON snapshot
store or CELL_FABRIC_STORE_KIND=journal for an append-only checksum-validated
journal. Production services can still provide RocksDB/Postgres-backed stores
behind the same IntentStore trait and, when the ckb-rpc-submit feature is
enabled, can use CkbRpcSubmitter behind the same CkbSubmitter trait.