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