Skip to content

a19q3/CellFabric

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CellFabric

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.

System Model

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:

  1. admit signed cell-native intents.
  2. index consumed cells, app conflict keys, dependencies, and replacements.
  3. quarantine visible conflicts without erasing evidence.
  4. select only conflict-free bundles.
  5. issue explicitly non-final soft confirmations.
  6. compile selected bundles into concrete CKB transactions.
  7. submit transactions to CKB and track L1 status.
  8. record Settled only after CKB commitment/finality, or Rejected if L1 explicitly rejects settlement material.

Threat Model

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.

External Reference Boundary: Obyte

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.

Architecture Invariants

  • 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-Spend Resolution Model

Double-spends are not eliminated by the DAG. They are made explicit and excluded from valid settlement.

The required flow is:

  1. Admit: accept the conflicting intent if its structure and auth binding are valid.
  2. Quarantine: record ConflictRecord entries by consumed OutPointRef and declared AppConflictKey.
  3. Exclude: bundle selection must never co-select conflicting intents.
  4. Evidence: proof service must be able to reconstruct the signed intents, conflict key, bundle lineage, and settlement lineage.
  5. 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.

Reentrancy And Purity Rules

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.

Decentralization Path

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:

  1. single orderer / devnet services over this deterministic core.
  2. multiple known orderers independently building and signing reproducible bundles.
  3. 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.

Scope

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-lock intents
  • 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 / App actions
  • 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 http feature exposing Axum routes for gateway/orderer/proof/submitter APIs
  • optional ckb-rpc-submit feature exposing a JSON-RPC send_transaction submitter
  • 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

Safety Invariants

  • SignedIntent::id is derived only from IntentBody, not from auth bytes.
  • all canonical hashed structures use stable BTreeMap / BTreeSet ordering.
  • hard conflicts are keyed by consumed OutPointRef.
  • app conflicts are explicit AppConflictKey values inside ResourceAccess.
  • 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 Bundle snapshots.
  • direct settlement compilation enforces MinOutputCapacity, ExactOutputLock, and declared MaxFee constraints for supported actions.
  • Swap and generic App intents 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_root commits 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.
  • SettlementPlanEvidence binds bundle id, data-availability root, plan hash, and concrete tx hashes for audit and dispute handoff.
  • SettlementFinalityEvidence binds 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 Rejected as 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.

Module Map

  • types: core CKB-facing domain types and canonical encoders
  • availability: bundle evidence manifest and data-availability roots
  • dag: intent graph, replacement edges, conflict indexes, snapshots
  • bundle: deterministic selector, bundle IDs, soft-confirmation receipts
  • validation: intent, bundle, unsigned receipt, and signed receipt validation
  • policy: app conflict policies, policy registry, replacement validation
  • auth: pluggable auth verifier registry and auth kind routing
  • ledger: event log and reduced user-visible status state
  • engine: synchronous core engine over an IntentStore
  • actor: Tokio single-writer command boundary around IntentEngine
  • gateway: ingress/query API trait and actor-backed adapter
  • orderer: bundle selection and non-final soft-confirmation adapter; validated orderers can inject app policies and auth verifiers before soft confirmation
  • receipt_auth: secp256k1 bundle receipt signing and verifier registry for non-final orderer evidence, plus receipt quorum policy
  • proof_service: actor-backed exit/dispute evidence construction
  • draft: direct CKB transaction draft builder for simple actions
  • compiler: reusable settlement compiler trait and direct compiler
  • app_compiler: app-specific settlement compiler registry and output fragments
  • settlement: serialized CKB tx bytes, settlement plans, plan hashes, and plan evidence commitments
  • recovery: settlement retry, recompile, and abandon policy decisions
  • fee_bump: deterministic bundle fee snapshotting, retry/recompile fee-bump planning, and explicit fee-bumped settlement-plan compiler hooks
  • submitter: CKB submission trait, dry-run submitter, lifecycle recorder, and recovery-aware submission service
  • worker: due settlement recovery scanner and bounded retry/recompile executor
  • tracker: L1 tx status provider trait, settlement observation recorder, and bundle settlement/finality evidence builder
  • tx_pool: tx-pool eviction detector trait and recovery monitor
  • rpc_submitter: optional CKB JSON-RPC send_transaction adapter behind --features ckb-rpc-submit; it also implements L1TxStatusProvider through get_transaction
  • http: optional Axum router and handlers behind --features http
  • evidence: exit/dispute proof skeletons
  • store: store abstraction, memory store, atomic JSON file store, and checksum-validated append-only journal store

Current Boundaries

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.

Optional Features

Run the full core tests:

cargo test -p cell-fabric

Enable the Axum dev server:

cargo test -p cell-fabric --features http
cargo run -p cell-fabric --features http --bin cell-fabric-http

Enable the CKB RPC submitter:

cargo test -p cell-fabric --features ckb-rpc-submit

Enable both integration surfaces:

cargo test -p cell-fabric --features "http ckb-rpc-submit"

Optional HTTP Server

The http feature exposes an Axum router and a small in-memory dev server:

cargo run -p cell-fabric --features http --bin cell-fabric-http

Environment variables:

  • CELL_FABRIC_HTTP_BIND, default 127.0.0.1:8118
  • CELL_FABRIC_CHAIN_ID, default ckb-dev
  • CELL_FABRIC_ACTOR_BUFFER, default 1024
  • CELL_FABRIC_STORE_PATH, optional file/journal store path
  • CELL_FABRIC_STORE_KIND, file or journal, default file
  • CELL_FABRIC_JOURNAL_SYNC, default true when CELL_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.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages