]> Untitled Git - bdk/commitdiff
Merge bitcoindevkit/bdk#2232: feat(core): allow serialization of SyncResponse and...
authormerge-script <hello@evanlinjin.me>
Wed, 11 Nov 2026 00:56:29 +0000 (11:56 +1100)
committermerge-script <hello@evanlinjin.me>
Wed, 11 Nov 2026 00:56:29 +0000 (11:56 +1100)
56d92eb79461925f2983983806f57f4532926363 feat(core): allow serialization of SyncResponse and FullScanResponse (Claude)

Pull request description:

  ### Description

  Adds `serde::Serialize` / `serde::Deserialize` for `SyncResponse` and `FullScanResponse` (under the `serde` feature). This enables offline workflows: an online device syncs or scans, serializes the response, and hands it to a device without internet access, which deserializes and applies it on its own. This is the goal of #1954, implemented without recursion.

  Both responses are made of a `TxUpdate` and an `Option<CheckPoint>` (plus `last_active_indices` for `FullScanResponse`), so those gain serde support too:

  - `TxUpdate`: a plain derive. Its fields are flat collections, so there is nothing self-referential to recurse over. `serde(default)` lets a newer reader accept data from an older writer that lacks a later-added field.
  - `CheckPoint`: implemented by hand. It is a reference-counted linked list, so a derived impl would recurse once per checkpoint and overflow the stack on long chains, the same hazard that forced the hand-written `Drop` (#1634).
    - Serialize walks the chain with `CheckPoint::iter()` and emits a flat, tip-first sequence of `(height, data)` pairs.
    - Deserialize rebuilds it with `CheckPoint::from_blocks(..)`, which re-derives the `skip`/`index` topology and rejects blocks that aren't in ascending height order (or, for data with a `prev_blockhash` such as `Header`, aren't linked).

  ### Notes to the reviewers

  - Alternative to #1954, addressing the "serializing `CheckPoint`s is a bit odd since it is a linked list" concern raised there: the list is flattened and rebuilt, so neither direction recurses.
  - Why responses rather than `ChangeSet`s (the route discussed on #1954 and bitcoindevkit/bdk_wallet#231): a `ChangeSet` only applies if both sides start from the same state, while a response is self-contained.
  - `checkpoint_serde_is_not_recursive` round-trips a 10,240-checkpoint chain on a 32 KiB thread stack, like `checkpoint_drop_is_not_recursive`. A recursive impl would overflow it.
  - `checkpoint_serde_rejects_unlinked_headers` covers the deserialize error path with `Header` data.
  - `serde_json` is added only as a dev-dependency of `bdk_core`, for the tests.

  ### Changelog notice

  - Added `serde::Serialize` and `serde::Deserialize` implementations for `SyncResponse`, `FullScanResponse`, `TxUpdate` and `CheckPoint` (under the `serde` feature). `CheckPoint` is (de)serialized iteratively as a flat list of `(height, data)` blocks.

  ### Checklists

  #### All Submissions:

  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)

  #### New Features:

  * [x] I've added tests for the new feature
  * [x] I've added docs for the new feature

  ---
  _Generated by [Claude Code](https://claude.ai/code/session_014BXMFRQP8qoGBghJcWzakG)_

ACKs for top commit:
  evanlinjin:
    self-ACK 56d92eb79461925f2983983806f57f4532926363

Tree-SHA512: fff75a10cd7a12ec7308699e674a03cba257e633b0feb1c58ccf42f04fb683dbfb17db0af0fae433272241e51d1d2e88f101059053bb74f59222edc10d615069


Trivial merge