Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,17 +119,17 @@ jobs:
cartesi-machine-sha256-arm64: ${{ env.CARTESI_MACHINE_SHA256_ARM64 }}
install-foundry: "true"

- name: Install faketime
- name: Install native test dependencies
run: |
sudo apt-get update
sudo apt-get install -y faketime libfaketime
sudo apt-get install -y faketime libfaketime libclang-dev

- name: Build watchdog Lua deps
run: |
sudo apt-get install -y libcurl4-openssl-dev build-essential pkg-config
just watchdog-lua-deps

- name: Run rollups E2E tests
- name: Run rollups E2E tests (Rust and C hosts)
run: just test-rollups-e2e

# Runs after the e2e step so the canonical machine image is already built;
Expand Down
5 changes: 4 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,10 @@ Paths below are relative to `sequencer/src/`:
mandatory offset with its source in `application_inputs`.
- **History version** — `(EraId, RecoveryGeneration)`. Setup publishes a complete
baseline with a fresh era; recovery increments the generation exactly once
iff it invalidates at least one valid batch. Subscription claims enforce both.
iff it invalidates at least one valid batch and records the preserved-prefix
cut in the same transaction. `/history` can check a saved checkpoint across
intervening generations; subscription claims still enforce both identifiers.
The [history contract](docs/protocol/application-history.md) owns compatibility.
- **Soft confirmation** — sequencer's predicted ordering, emitted before the batch lands on L1.
- **Snapshot** — immutable artifact at every batch close, registered with its
local batch identity and application count. Acceptance facts select the
Expand Down
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

127 changes: 127 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,133 @@ Message shapes:
{ "kind": "direct_input", "offset": 11, "sender": "0x...", "block_number": 123, "block_timestamp": 1700000000, "transaction_hash": "0x...", "payload": "0x...", "input_index": 42, "batch_nonce": 4 }
```

### History metadata and historical L1 inputs (internal only)

Readers that maintain additional transfer/order history can reconstruct it from
L1 and then join the application feed. The
[projection replay contract](docs/protocol/projection-replay.md) describes
bootstrap, client checkpoints, pending directs, and terminal drain.

`GET /history` returns one coherent view of the deployment, current application
history, immutable era baseline, and latest accepted checkpoint. Optional
`era_id=<uuid>` requires the selected era; a mismatch returns `409 ERA_CHANGED`.
Example immediately after a rebuild:

```json
{
"deployment": {
"chain_id": 31337,
"app_address": "0x1111111111111111111111111111111111111111",
"input_box_address": "0x2222222222222222222222222222222222222222",
"app_deployment_block": 1,
"batch_submitter_address": "0x3333333333333333333333333333333333333333"
},
"history": {
"version": {
"era_id": "22222222-2222-4222-8222-222222222222",
"recovery_generation": 0
},
"available_from": 7,
"head": 7
},
"baseline": {
"l1_stop_block": 1240,
"l1_end_input_index": 8,
"next_batch_nonce": 2
},
"accepted_checkpoint": null,
"compatibility": null
}
```

- `history.available_from` is baseline application count `K`; entries `[K,head)`
are available through WS. Counts include all executed application inputs.
- `baseline` describes the fixed L1 stopping block `C`, exclusive InputBox end
`R`, and scheduler nonce after recovery's terminal drain. It survives generation
changes and baseline artifact GC. It is distinct from the moving safe head.
- `accepted_checkpoint`, when available, has `inclusion_block`,
`executed_input_count`, and `next_batch_nonce`, under `history.version`.
Genesis supplies the zero checkpoint; a rebuilt baseline is not itself an
accepted checkpoint. The metadata does not lease or download a native artifact
and does not certify a client projection. A known divergence returns `503`.
- `compatibility` is `null` unless `from_generation=<u64>` is supplied together
with `era_id`. It then contains `from_generation` and `preserved_input_count`:
the prefix that survived every standard recovery since that generation,
bounded by the current head. A future generation or missing era returns
`400 BAD_REQUEST`; an era mismatch takes precedence over the generation bound.

For example, `GET /history?era_id=<uuid>&from_generation=0` can return
`"compatibility": {"from_generation": 0, "preserved_input_count": 3}`.
A saved checkpoint from that era/generation is reusable when its count `X`
satisfies `K <= X <= 3`. The boundary is inclusive: the checkpoint has executed
entries before `X`, and resumes at entry `X`. Each checkpoint must be checked
using its own saved generation. With no intervening recovery, the bound is the
current head. The [history contract](docs/protocol/application-history.md#checkpoint-compatibility-after-standard-recovery)
defines the calculation and trust boundary.

Restore an eligible checkpoint, persist the response's current history version
with it, and subscribe using that version and its actual count. A recovery
between lookup and subscription still returns `STALE_GENERATION`; repeat the
lookup using the version associated with the restored state. Compatibility does
not certify the client's application or projection implementation, and cannot
cross a cockroach recovery's new era.

`GET /historical-l1-inputs` requires `era_id` and exactly one starting selector:

- `next_input_index=<u64>`: inclusive per-application InputBox index, starting at 0.
- `after_block=<u64>`: initially seek to the first input strictly after that block;
continue using the returned `next_input_index`.

The endpoint serves only `[0,R)` through the selected era's `C`. A response to
`next_input_index=5&limit=1` can be:

```json
{
"era_id": "22222222-2222-4222-8222-222222222222",
"l1_stop_block": 1240,
"end_input_index": 8,
"next_input_index": 6,
"items": [{
"input_index": 5,
"sender": "0x3333333333333333333333333333333333333333",
"payload": "0x00",
"block_number": 1230,
"block_timestamp": 1700014760,
"transaction_hash": "0x4444444444444444444444444444444444444444444444444444444444444444"
}]
}
```

Records preserve original inner payloads and authenticated senders, including
malformed/rejected batches; they are not complete `EvmAdvance` envelopes. Indices
are contiguous and ordered. Binary values are hex; timestamps are Unix seconds.
Clients must preserve integer precision. A page may split a block.

Optional `limit` defaults to 256 and accepts 1–256. Pages target 1 MiB of raw
payloads; a larger first input is returned alone, intact. Hex encoding increases
wire size, so this is not a hard response-size limit. Eight historical responses
can be in flight; a permit remains held through body delivery or cancellation.
SQLite read transactions end before network delivery. These limits bound memory
by the page target or largest single input, not total history length.

Only `next_input_index == end_input_index` means EOF; a short page does not.
Requesting `next_input_index=R` or `after_block=C` returns an empty completed page.
Generation changes do not invalidate historical pages; an era change does.

Malformed/unknown query fields, invalid selectors/limits, or positions above
`R`/`C` return the existing `400 BAD_REQUEST` JSON shape. An era mismatch returns
the existing `409 ERA_CHANGED` history-policy body before semantic position
checks. Capacity exhaustion returns `429 OVERLOADED`; shutdown or an operational
read failure returns `503 UNAVAILABLE`. Interrupted bodies are failed pages.
Missing durable rows or other storage invariant failures follow the process's
terminal fault policy, never a successful partial page.

The Rust SDK exposes `history(expected_era, from_generation)` and
`historical_l1_inputs(era, start, limit)` with typed metadata and era refusals.
Both use the configured request deadline, including body transfer; callers may
increase it for large historical inputs. The client owns replay, persistence,
checkpoint selection, and subscription.

### Operator snapshot endpoints (internal only)

These serve application state to the operator's watchdog and indexers.
Expand Down
20 changes: 20 additions & 0 deletions bindings/c-app-engine/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,23 @@ notices and vouchers, rejection/no-op progress, dump round trips, independent
instances, and fatal/error classification.
`cargo test -p c-app-engine --lib` also checks mixed-output ordering, copying
reused engine buffers, and full-width voucher values with a small ABI fixture.

The `c_host_` scenarios in `rollups-e2e` launch the reference C host with generated
devnet genesis, delete that source before startup, and exercise ordinary execution,
clean restart, stale recovery, and checkpoint-based rebuild. Independent
`EngineApp` replicas restore HTTP archives, discard the downloaded sources,
follow backlog and live WS inputs, and check history identity across recovery.
Ordinary execution and both recovery paths also compare the host against the
canonical machine. Run them with:

```sh
just setup
just ensure-machine-image
cargo build --locked -p c-wallet-engine --bin c-wallet-genesis -p c-wallet-sequencer --bin c-wallet-sequencer -p rollups-e2e --bin rollups-e2e
target/debug/rollups-e2e c_host_ --nocapture
```

`just test-rollups-e2e` includes these scenarios in CI. This covers the reference
wallet across the C ABI; private engines still need their own integration and
canonical comparison. Rebuilding from a native recovery archive does not test
the application's canonical-machine-to-native exporter.
9 changes: 6 additions & 3 deletions docs/invariants.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ by writer and are write-once (`0001_schema.sql`).
| inclusion lane | `batches` (insert + `sealed_at_ms`), `frames`, `user_ops`, `application_inputs`, `dumps`/`snapshots` (batch close) |
| input reader | `safe_inputs`, `l1_safe_head`, `safe_accepted_batches`, `canonical_divergence` (the divergence poison marker) |
| recovery (startup) | `batches.invalidated_at_ms`, Tip reopen, current `application_inputs` suffix deletion |
| history metadata (setup/recovery) | `history_state` — complete era/application-count/L1-block baseline, generation bump in a non-empty standard-recovery cascade |
| history metadata (setup/recovery) | `history_state` — complete era/application-count/L1-block baseline; generation advance and immutable preserved-prefix cut in a non-empty standard-recovery cascade |
| batch submitter and mempool flusher | `wallet_nonce_watermark` — deliberately shared under one protocol: each raises it before its first broadcast (write-before-broadcast, I14) |
| egress (HTTP) | `dumps.lease_count` (leases); `run`'s startup hygiene resets it to zero as the crash backstop |
| setup | `deployment_identity` (pinned once), `batch_tree_anchor` (the root nonce, frozen once setup completes), the initial `dumps` + `snapshots` rows (genesis or rebuild registration, atomic with the complete history baseline), the `setup_complete` fact (written once), `batch_policy.log_gas_price` + `log_gas_price_updated_at_ms` (first write; Fixed and Uniswap) |
Expand Down Expand Up @@ -160,7 +160,7 @@ by writer and are write-once (`0001_schema.sql`).
### I5. Recovery removes exactly the invalidated application suffix

- **Holds:** invalidating a batch deletes its `application_inputs` through the
schema trigger. The cascade, generation increment, and replacement Tip commit
schema trigger. The cascade, generation cut/increment, and replacement Tip commit
together. Original source records and immutable snapshots remain; snapshot
selection excludes invalidated batches and GC retires their unleased artifacts.
- **Enforced by:** `cascade_and_reopen`, application-input constraints, valid views.
Expand Down Expand Up @@ -425,7 +425,10 @@ by writer and are write-once (`0001_schema.sql`).
transaction. The history row is absent before this boundary. `K` and `C`
remain immutable even after baseline artifact GC or recovery-root invalidation.
- **Standard recovery:** one generation increment iff a valid batch is
invalidated, in the cascade transaction. Clean restart changes neither token.
invalidated, with an immutable cut at the count after suffix deletion and
before replacement directs. The entire transition commits in the cascade
transaction. Clean restart changes neither token. Every intervening cut is
required to authorize reusing a checkpoint from an older generation.
- **Enforced by:** `complete_baseline_setup`, immutable history triggers,
exact-`+1` generation trigger, and `cascade_and_reopen`.
- **Depended on by:** mandatory snapshot-derived WS claims. Identity is validated
Expand Down
43 changes: 29 additions & 14 deletions docs/plans/2026-07-coordination-tracks.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ freely at this stage — no backward-compatibility constraints.

| # | Track | Owner | Status |
|---|-------|-------|--------|
| 3 | Feed & replay protocol redesign | us (design) → us/Stephen (impl) | **implemented** — canonical application history, snapshot restore archives, mandatory WS claims, typed refusals, and SDK cutover; [remaining integration gates](2026-07-track3-feed-replay-design.md#remaining-integration-gates) |
| 3 | Feed & replay protocol redesign | us (design) → us/Stephen (impl) | **repository API implemented; post-merge adoption and deployment work remain** — [follow-up sequence](2026-07-track3-feed-replay-design.md#follow-up-sequence) and [ownership](2026-07-track3-feed-replay-design.md#merge-scope-and-follow-up-ownership) |
| 5 | Fee exponentiation LUT | us | **deferred** — decided exact-floor if built (the table *is* the spec, algorithm-free; replay continuity across the upgrade explicitly not preserved); a separate pending design decision may make log-space fees defunct — revisit after syncing with Bart |
| 6 | Dump / `Application` API redesign | us + Bart | **interface and reference C binding implemented** — [Application contract](../protocol/application-contract.md); native-engine integration gates remain |

**Current campaign order:**

1. Validate snapshot-to-live replica bootstrap through the reference C bridge, then the private DEX engine when shared.
2. Remeasure feed latency in the representative environment.
1. Merge the implemented egress API after repository review/checks. Bart can then integrate his client; adjust the API from concrete feedback without waiting for downstream completion.
2. Application integrators/operators validate the private engine, canonical-to-native exporter and recovery drill, and representative capacity before production use. Reference C-host lifecycle coverage is part of repository CI.
3. Track 5 (fee LUT) only after the log-space-fees decision.

Full restore archives now support file and directory application prefixes.
Expand All @@ -33,9 +33,15 @@ bootstrap, history identity, replay, and recovery boundaries. The
and canonical recovery/watchdog gates have a
[validation record](../review/2026-09-16-track3-validation.md).

Remaining work is native-engine integration and representative deployment
latency, tracked in the [integration plan](2026-07-track3-feed-replay-design.md).
Additional transport or retention mechanisms require a measured consumer need.
Readers whose projections contain information absent from the latest application
state can use the implemented fixed-prefix historical L1 API and checkpoint
metadata. The [projection contract](../protocol/projection-replay.md) owns that
workflow; the history contract owns implemented checkpoint compatibility across
standard recoveries. The [integration plan](2026-07-track3-feed-replay-design.md)
owns follow-up requirements and their owners. Native-engine integration and
representative deployment latency remain open after merge; they are not egress
API merge prerequisites. Other transport or retention mechanisms require a
measured need.

## Track 5 — Fee exponentiation LUT (deferred)

Expand All @@ -57,17 +63,26 @@ until the pending log-space-fees decision lands (with Bart).

The [Application contract](../protocol/application-contract.md) owns execution,
engine progress, and checkpoint semantics. The [C binding guide](../protocol/c-application-binding.md)
maps that contract to native engines; its reference conformance suite is
implemented. End-to-end native snapshot-to-live bootstrap remains an integration
gate, alongside the private DEX engine when available. Reference bridge
conformance cannot establish private-engine correctness.
maps that contract to native engines. Its reference conformance suite and C-host
process scenarios cover snapshot-to-live bootstrap, restart, standard recovery,
and fresh-era rebuild with canonical comparison. Reference bridge conformance
cannot establish private-engine correctness.

Remaining checks need the actual consumer:

- Exercise snapshot-to-live bootstrap and canonical comparison through the C
host in CI; its current smoke test builds and invokes `--help`. A reusable
conformance runner needs engine-supplied genesis and meaningful accepted and
rejected inputs. Compare canonical state files, not recovery-dump layouts.
- Supply the application's versioned canonical-machine-to-native recovery
exporter and completed operator runbook. Require the
[non-genesis recovery drill](../recovery/cockroach.md#recovery-readiness-before-deployment)
for production readiness: the old native state is unavailable, the exported
bundle restores correctly, and execution after rebuild matches the canonical
machine. For the DEX, pin the designated state drive/memory region and derive
resume metadata from canonical execution. Add the integration check to the
release validation once the actual artifacts are available; no generic trait
or deployment gate currently enforces this requirement.
- Repeat snapshot-to-live bootstrap and canonical comparison with the private
engine's genesis and meaningful accepted/rejected inputs. The reference
`c_host_` scenarios supply the lifecycle pattern; compare canonical state
files, not recovery-dump layouts.
- Verify the external scheduler's ordering, fee conversion, and recovery
agreement. Publish independent-port fee vectors for the
[current arithmetic](../../sequencer-core/src/fee.rs); a deferred LUT is a
Expand Down
Loading
Loading