Home / TxPipe / dolos
Apr 29, 1-2 PM (1)
Apr 29, 2-3 PM (0)
Apr 29, 3-4 PM (0)
Apr 29, 4-5 PM (0)
Apr 29, 5-6 PM (1)
Apr 29, 6-7 PM (2)
Apr 29, 7-8 PM (0)
Apr 29, 8-9 PM (0)
Apr 29, 9-10 PM (0)
Apr 29, 10-11 PM (1)
Apr 29, 11-12 AM (5)
Apr 30, 12-1 AM (0)
Apr 30, 1-2 AM (0)
Apr 30, 2-3 AM (0)
Apr 30, 3-4 AM (0)
Apr 30, 4-5 AM (0)
Apr 30, 5-6 AM (0)
Apr 30, 6-7 AM (0)
Apr 30, 7-8 AM (0)
Apr 30, 8-9 AM (0)
Apr 30, 9-10 AM (0)
Apr 30, 10-11 AM (1)
Apr 30, 11-12 PM (0)
Apr 30, 12-1 PM (0)
Apr 30, 1-2 PM (0)
Apr 30, 2-3 PM (0)
Apr 30, 3-4 PM (0)
Apr 30, 4-5 PM (0)
Apr 30, 5-6 PM (0)
Apr 30, 6-7 PM (0)
Apr 30, 7-8 PM (0)
Apr 30, 8-9 PM (0)
Apr 30, 9-10 PM (0)
Apr 30, 10-11 PM (0)
Apr 30, 11-12 AM (0)
May 01, 12-1 AM (0)
May 01, 1-2 AM (0)
May 01, 2-3 AM (0)
May 01, 3-4 AM (0)
May 01, 4-5 AM (0)
May 01, 5-6 AM (0)
May 01, 6-7 AM (0)
May 01, 7-8 AM (0)
May 01, 8-9 AM (0)
May 01, 9-10 AM (0)
May 01, 10-11 AM (0)
May 01, 11-12 PM (0)
May 01, 12-1 PM (0)
May 01, 1-2 PM (0)
May 01, 2-3 PM (0)
May 01, 3-4 PM (0)
May 01, 4-5 PM (0)
May 01, 5-6 PM (0)
May 01, 6-7 PM (0)
May 01, 7-8 PM (0)
May 01, 8-9 PM (0)
May 01, 9-10 PM (0)
May 01, 10-11 PM (0)
May 01, 11-12 AM (0)
May 02, 12-1 AM (0)
May 02, 1-2 AM (0)
May 02, 2-3 AM (0)
May 02, 3-4 AM (0)
May 02, 4-5 AM (0)
May 02, 5-6 AM (0)
May 02, 6-7 AM (0)
May 02, 7-8 AM (0)
May 02, 8-9 AM (0)
May 02, 9-10 AM (0)
May 02, 10-11 AM (0)
May 02, 11-12 PM (0)
May 02, 12-1 PM (0)
May 02, 1-2 PM (0)
May 02, 2-3 PM (0)
May 02, 3-4 PM (1)
May 02, 4-5 PM (0)
May 02, 5-6 PM (0)
May 02, 6-7 PM (1)
May 02, 7-8 PM (1)
May 02, 8-9 PM (0)
May 02, 9-10 PM (0)
May 02, 10-11 PM (0)
May 02, 11-12 AM (0)
May 03, 12-1 AM (0)
May 03, 1-2 AM (0)
May 03, 2-3 AM (0)
May 03, 3-4 AM (0)
May 03, 4-5 AM (0)
May 03, 5-6 AM (0)
May 03, 6-7 AM (0)
May 03, 7-8 AM (0)
May 03, 8-9 AM (0)
May 03, 9-10 AM (0)
May 03, 10-11 AM (0)
May 03, 11-12 PM (0)
May 03, 12-1 PM (0)
May 03, 1-2 PM (0)
May 03, 2-3 PM (0)
May 03, 3-4 PM (0)
May 03, 4-5 PM (0)
May 03, 5-6 PM (0)
May 03, 6-7 PM (0)
May 03, 7-8 PM (0)
May 03, 8-9 PM (0)
May 03, 9-10 PM (0)
May 03, 10-11 PM (0)
May 03, 11-12 AM (0)
May 04, 12-1 AM (0)
May 04, 1-2 AM (0)
May 04, 2-3 AM (0)
May 04, 3-4 AM (0)
May 04, 4-5 AM (0)
May 04, 5-6 AM (0)
May 04, 6-7 AM (1)
May 04, 7-8 AM (0)
May 04, 8-9 AM (0)
May 04, 9-10 AM (0)
May 04, 10-11 AM (0)
May 04, 11-12 PM (0)
May 04, 12-1 PM (0)
May 04, 1-2 PM (1)
May 04, 2-3 PM (2)
May 04, 3-4 PM (0)
May 04, 4-5 PM (0)
May 04, 5-6 PM (0)
May 04, 6-7 PM (0)
May 04, 7-8 PM (0)
May 04, 8-9 PM (0)
May 04, 9-10 PM (0)
May 04, 10-11 PM (0)
May 04, 11-12 AM (0)
May 05, 12-1 AM (0)
May 05, 1-2 AM (0)
May 05, 2-3 AM (0)
May 05, 3-4 AM (0)
May 05, 4-5 AM (0)
May 05, 5-6 AM (0)
May 05, 6-7 AM (0)
May 05, 7-8 AM (0)
May 05, 8-9 AM (0)
May 05, 9-10 AM (0)
May 05, 10-11 AM (4)
May 05, 11-12 PM (0)
May 05, 12-1 PM (0)
May 05, 1-2 PM (0)
May 05, 2-3 PM (1)
May 05, 3-4 PM (3)
May 05, 4-5 PM (0)
May 05, 5-6 PM (1)
May 05, 6-7 PM (0)
May 05, 7-8 PM (0)
May 05, 8-9 PM (0)
May 05, 9-10 PM (0)
May 05, 10-11 PM (0)
May 05, 11-12 AM (0)
May 06, 12-1 AM (0)
May 06, 1-2 AM (0)
May 06, 2-3 AM (0)
May 06, 3-4 AM (0)
May 06, 4-5 AM (0)
May 06, 5-6 AM (0)
May 06, 6-7 AM (0)
May 06, 7-8 AM (0)
May 06, 8-9 AM (0)
May 06, 9-10 AM (0)
May 06, 10-11 AM (0)
May 06, 11-12 PM (0)
May 06, 12-1 PM (0)
May 06, 1-2 PM (0)
26 commits this week Apr 29, 2026 - May 06, 2026
refactor(cardano): rename is_replay_noop to is_complete, share with initialize
The predicate was already derived from start_shard >= total_shards but
its name described the consequence in finalize ("replay no-op") rather
than the state ("shard work is complete"). With the state framing, the
same predicate is meaningful in initialize too: assign the fields
unconditionally from progress, then check is_complete() for the
short-circuit. Removes the special-cased branch that previously set
start_shard = p.total inline.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
docs(cardano): address coderabbit review on PR #991
- Fix EpochWrapUpV2 docstring: variant index is 42, not 40 (counting
  past EWrapProgress=39, EStartProgress=40, RupdProgress=41).
- Add TODO(wal-compat) block above EpochWrapUpV2 mirroring the V1
  block, so the V3 cutover has the same documented removal trigger
  ("no on-disk WAL row references variant index 42").
- Document is_replay_noop() precondition: must be called after
  initialize(); a freshly-new()'d unit would spuriously report true.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
fix(cardano): short-circuit ewrap replay between boundary phases
EpochState had no flag distinguishing "EWRAP not started" from "EWRAP
done, ESTART pending finalize" — both showed `ewrap_progress = None`.
A crash between EWRAP finalize and ESTART finalize left the cursor
behind the boundary block (cursor only advances at ESTART finalize),
so the next live block re-triggered EWRAP for the same boundary. The
replay ran visitors over partially-rotated accounts and panicked at
`PoolDelegatorRetire::apply`'s `unreachable!`.

Add `EpochWrapUpV3` that no longer clears `ewrap_progress` on apply —
the field stays at `Some(total, total)` after EWRAP finalize and is
cleared only by `EpochTransitionV2` at ESTART finalize. That single-
writer invariant makes `committed == total` the unambiguous "EWRAP
done, ESTART pending" marker. `EwrapWorkUnit::initialize` reads it,
sets `start_shard = total_shards` to skip the shard loop, and a new
`is_replay_noop()` accessor lets `finalize` no-op too. The next
boundary trigger falls through to ESTART, which resumes from
`estart_progress.committed` via the existing per-shard path.

V2 stays around `#[deprecated]` for WAL replay of historical rows,
mirroring the V1 → V2 cutover pattern.

Follow-up TODOs left at `PoolDelegatorRetire::apply` and
`DRepDelegatorDrop::apply` for the deeper hardening (read via
`snapshot_at(self.epoch)`; guard schedule on `entity.epoch() ==
Some(self.epoch)`) — the recovery short-circuit removes the known
trigger but the visitor/delta read divergence is still fragile.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
feat(cardano): stage van Rossem (protocol v11) migration arm
Add (10, 11) entry to migrate_pparams_version so a v10→v11 boundary
no longer panics in force_pparams_version. Van Rossem is an intra-
Conway-era hardfork (no new pparams, certs, gov actions, or CBOR
shape), so intra_era_hardfork is the correct migration helper.

Ledger-side van Rossem support (V1/V2 cost-model backport, VRF-key
uniqueness in pool registration, reference-input validation update,
strict CostModels validation in PParamsUpdate) is tracked upstream
in pallas issues #752, #753, #754, #755 and is gated on those.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
feat(u5c): serve v1alpha and v1beta gRPC side-by-side (#987)
Register both UTxORPC v1alpha and v1beta service implementations on the
same listener. Clients pick which proto package they call; on the wire
the two are entirely separate gRPC services (utxorpc.v1alpha.* vs
utxorpc.v1beta.*), so no routing logic is needed and v1alpha bytes stay
unchanged.

Layout:
- src/serve/grpc/v1alpha/{query,sync,watch,submit}.rs - moved from
  src/serve/grpc/, imports rewritten to explicit pallas v1alpha paths.
- src/serve/grpc/v1beta/{query,sync,watch,submit}.rs - copies with
  imports swapped to v1beta. The only body divergence is in
  v1beta/query.rs read_utxos, where Datum.original_cbor is
  Option<Bytes> rather than Bytes.
- mod.rs: registers eight server impls (4 services x 2 versions) and
  10 file descriptor sets in tonic reflection.

Cargo.toml: bumps pallas to git rev 0da71c2 to pick up v1alpha and
v1beta proto packages and the v1beta::Mapper.

Drive-by: drop unused _mapper field from SubmitServiceImpl, and remove
two stale commented-out blocks in sync.rs.
feat(u5c): serve v1alpha and v1beta gRPC side-by-side
Register both UTxORPC v1alpha and v1beta service implementations on the
same listener. Clients pick which proto package they call; on the wire
the two are entirely separate gRPC services (utxorpc.v1alpha.* vs
utxorpc.v1beta.*), so no routing logic is needed and v1alpha bytes stay
unchanged.

Layout:
- src/serve/grpc/v1alpha/{query,sync,watch,submit}.rs - moved from
  src/serve/grpc/, imports rewritten to explicit pallas v1alpha paths.
- src/serve/grpc/v1beta/{query,sync,watch,submit}.rs - copies with
  imports swapped to v1beta. The only body divergence is in
  v1beta/query.rs read_utxos, where Datum.original_cbor is
  Option<Bytes> rather than Bytes.
- mod.rs: registers eight server impls (4 services x 2 versions) and
  10 file descriptor sets in tonic reflection.

Cargo.toml: bumps pallas to git rev 0da71c2 to pick up v1alpha and
v1beta proto packages and the v1beta::Mapper.

Drive-by: drop unused _mapper field from SubmitServiceImpl, and remove
two stale commented-out blocks in sync.rs.
docs: reconcile schema and minibf coverage with code (#986)
Add `update-docs` skill describing the source-of-truth for each
user-facing doc (config schema, CLI, MiniBF/MiniKupo route lists), and
apply the drift fixes it surfaces:

- minibf.mdx: add `/accounts/{stake_address}/withdrawals`, `/pools/{id}`,
  `/pools/{id}/metadata` rows (present in `build_router_with_facade`).
- schema.mdx: correct `serve.minikupo` example port from 3100 to 1442
  (matching the `init` default), and add the missing `include_minikupo`
  bullet under `logging`.
docs: reconcile schema and minibf coverage with code
Add `update-docs` skill describing the source-of-truth for each
user-facing doc (config schema, CLI, MiniBF/MiniKupo route lists), and
apply the drift fixes it surfaces:

- minibf.mdx: add `/accounts/{stake_address}/withdrawals`, `/pools/{id}`,
  `/pools/{id}/metadata` rows (present in `build_router_with_facade`).
- schema.mdx: correct `serve.minikupo` example port from 3100 to 1442
  (matching the `init` default), and add the missing `include_minikupo`
  bullet under `logging`.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
chore(deps): bump docker/setup-buildx-action in /.github/workflows (#969)
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3 to 4.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
fix(sync): apply deltas before WAL commit so rollback undo works (#985)
The roll work-unit lifecycle wrote the WAL before `apply_entities` ran, so
each on-disk delta carried `prev_*: None`. When `core/sync.rs::rollback`
later deserialized those rows and called `delta.undo()`, the first
non-trivial delta (typically `ControlledAmountInc`) panicked with
`panicked at crates/cardano/src/model/accounts.rs:329:47: apply captured stake`.
Pre-existing — the ordering was set when work units were formalized
(#842) but the panic was latent until #971 replaced no-op `undo`
stubs with real `expect("apply captured ...")` calls.

Reshuffles `RollWorkUnit` so `load_entities` runs in `load`,
`apply_entities` runs in `compute`, and `commit_wal` then serializes
deltas that already carry their `prev_*` undo state. `commit_state`
becomes a thin "persist what's already in memory" pass.

Adds an `applied: bool` invariant flag on `WorkBatch` with a
`debug_assert!` in `commit_wal` so a future re-ordering trips
locally instead of in production rollback.

Also fixes a related correctness issue exposed once `prev_*` actually
becomes load-bearing: `core/sync.rs::rollback` iterated deltas in
forward order when undoing. Multiple deltas keyed to the same entity
have to be reversed last-first to walk back through the apply chain
correctly. Inner loop is now `.iter_mut().rev()`.

New regression coverage:
- `assert_delta_serde_roundtrip` proptest helper that serializes the
  post-apply delta with bincode (the WAL's encoding), deserializes,
  and asserts `undo` restores the original entity. The existing
  `assert_delta_roundtrip` only round-trips in memory, so it never
  exercised the wire format.
- Serde-roundtrip proptests for `ControlledAmountInc`,
  `ControlledAmountDec`, `StakeRegistration`, `StakeDelegation`,
  `StakeDeregistration`, `VoteDelegation`, `WithdrawalInc`.
- Integration test `test_rollback_after_full_sync_lifecycle` that
  feeds blocks through the full sync lifecycle and rolls back. On
  the un-fixed code it dies with the exact panic from the bug
  report; with the fix it passes cleanly.

Out of scope (flagged for follow-up):
- Boundary work units (Ewrap, Estart, Rupd) don't write deltas to
  the WAL, so rollbacks across an epoch boundary still don't undo
  the boundary deltas.
- State catch-up from WAL after a crash between commit_wal and
  commit_state — same recovery window as before this fix; relies
  on the peer re-sending the block.
fix(sync): apply deltas before WAL commit so rollback undo works
The roll work-unit lifecycle wrote the WAL before `apply_entities` ran, so
each on-disk delta carried `prev_*: None`. When `core/sync.rs::rollback`
later deserialized those rows and called `delta.undo()`, the first
non-trivial delta (typically `ControlledAmountInc`) panicked with
`panicked at crates/cardano/src/model/accounts.rs:329:47: apply captured stake`.
Pre-existing — the ordering was set when work units were formalized
(#842) but the panic was latent until #971 replaced no-op `undo`
stubs with real `expect("apply captured ...")` calls.

Reshuffles `RollWorkUnit` so `load_entities` runs in `load`,
`apply_entities` runs in `compute`, and `commit_wal` then serializes
deltas that already carry their `prev_*` undo state. `commit_state`
becomes a thin "persist what's already in memory" pass.

Adds an `applied: bool` invariant flag on `WorkBatch` with a
`debug_assert!` in `commit_wal` so a future re-ordering trips
locally instead of in production rollback.

Also fixes a related correctness issue exposed once `prev_*` actually
becomes load-bearing: `core/sync.rs::rollback` iterated deltas in
forward order when undoing. Multiple deltas keyed to the same entity
have to be reversed last-first to walk back through the apply chain
correctly. Inner loop is now `.iter_mut().rev()`.

New regression coverage:
- `assert_delta_serde_roundtrip` proptest helper that serializes the
  post-apply delta with bincode (the WAL's encoding), deserializes,
  and asserts `undo` restores the original entity. The existing
  `assert_delta_roundtrip` only round-trips in memory, so it never
  exercised the wire format.
- Serde-roundtrip proptests for `ControlledAmountInc`,
  `ControlledAmountDec`, `StakeRegistration`, `StakeDelegation`,
  `StakeDeregistration`, `VoteDelegation`, `WithdrawalInc`.
- Integration test `test_rollback_after_full_sync_lifecycle` that
  feeds blocks through the full sync lifecycle and rolls back. On
  the un-fixed code it dies with the exact panic from the bug
  report; with the fix it passes cleanly.

Out of scope (flagged for follow-up):
- Boundary work units (Ewrap, Estart, Rupd) don't write deltas to
  the WAL, so rollbacks across an epoch boundary still don't undo
  the boundary deltas.
- State catch-up from WAL after a crash between commit_wal and
  commit_state — same recovery window as before this fix; relies
  on the peer re-sending the block.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>