Skip to content

feat(core): allow serialization of SyncResponse#22

Open
evanlinjin wants to merge 1 commit into
masterfrom
claude/happy-darwin-hkwqlc
Open

feat(core): allow serialization of SyncResponse#22
evanlinjin wants to merge 1 commit into
masterfrom
claude/happy-darwin-hkwqlc

Conversation

@evanlinjin

Copy link
Copy Markdown
Owner

Description

Adds serde::Serialize / serde::Deserialize for SyncResponse, enabling air-gapped workflows: a connected (online) device can sync, serialize the SyncResponse, and hand it to a disconnected (offline) device which deserializes and applies it independently. This is the goal of bitcoindevkit#1954, implemented without recursion.

SyncResponse is made up of a TxUpdate and an Option<CheckPoint>, so both gain serde support:

  • TxUpdate — a plain #[derive(Serialize, Deserialize)]. Its fields are flat collections, so there is nothing self-referential to recurse over. An Ord bound is added on A for deserialization so that anchors: BTreeSet<(A, Txid)> can be rebuilt.

  • CheckPoint — this is the interesting one. A CheckPoint is a reference-counted linked list (prev + skip pointers), so a derived implementation would recurse one stack frame per checkpoint and overflow the stack on long chains — the exact hazard that already forced a hand-written Drop (example_bitcoind_rpc_polling sync command ends with stack overflow bitcoindevkit/bdk#1634). It is therefore implemented by hand:

    • Serialize: walk the chain iteratively via CheckPoint::iter() and emit a flat sequence of (height, data) pairs.
    • Deserialize: collect the sequence and rebuild with CheckPoint::from_blocks(..), which re-derives the skip/index topology deterministically.

    The skip/index topology is intentionally not serialized — it is reconstructed on load, keeping the encoding minimal and both directions iterative.

Notes to the reviewers

  • This is an alternative take on core: allow serialization of SyncResponse bitcoindevkit/bdk#1954 that addresses the "serializing CheckPoints is a bit odd since it is a linked list" concern raised in review: the linked list is flattened to a (height, data) sequence and rebuilt, so neither serialization nor deserialization recurses.
  • checkpoint_serde_is_not_recursive round-trips a 10k-checkpoint chain on a deliberately small (128 KiB) thread stack, mirroring the existing checkpoint_drop_is_not_recursive test — a recursive impl would overflow it.
  • serde_json is added only as a dev-dependency of bdk_core, for the round-trip tests.
  • Scope is intentionally limited to SyncResponse to match core: allow serialization of SyncResponse bitcoindevkit/bdk#1954. FullScanResponse can follow the exact same pattern as a trivial follow-up if desired.

Changelog notice

  • Added serde::Serialize and serde::Deserialize implementations for SyncResponse, TxUpdate, and CheckPoint (under the serde feature). CheckPoint is (de)serialized iteratively as a flat list of (height, data) blocks to avoid recursing over its linked-list structure.

Checklists

All Submissions:

New Features:

  • I've added tests for the new feature
  • I've added docs for the new feature

Generated by Claude Code

Add `serde::Serialize`/`Deserialize` for `SyncResponse` so a connected
device can serialize a sync response and hand it to an air-gapped device
to apply independently.

`SyncResponse` holds a `TxUpdate` and an `Option<CheckPoint>`:

- `TxUpdate` gains a plain derive; its fields are flat collections so
  there is nothing self-referential to recurse over.
- `CheckPoint` is a reference-counted linked list, so a derived impl
  would recurse through `prev`/`skip` once per checkpoint and overflow
  the stack on long chains -- the same hazard that forced the
  hand-written `Drop` (bitcoindevkit#1634). It is instead serialized iteratively as a
  flat sequence of `(height, data)` pairs and rebuilt with
  `CheckPoint::from_blocks`, which re-derives the `skip`/`index`
  topology deterministically.

Tests cover round-tripping `CheckPoint`, `TxUpdate` and `SyncResponse`,
and assert (de)serialization is not recursive by round-tripping a long
chain on a deliberately small thread stack.

https://claude.ai/code/session_014BXMFRQP8qoGBghJcWzakG
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants