Skip to content

wallet/sql: implement waddrmgr store with sqlc - #1296

Draft
Roasbeef wants to merge 3 commits into
sql-port-stage2-wtxmgrfrom
sql-port-stage2-waddrmgr
Draft

wallet/sql: implement waddrmgr store with sqlc#1296
Roasbeef wants to merge 3 commits into
sql-port-stage2-wtxmgrfrom
sql-port-stage2-waddrmgr

Conversation

@Roasbeef

@Roasbeef Roasbeef commented Jul 14, 2026

Copy link
Copy Markdown
Member

Overview

In this PR, we complete the waddrmgr side of the SQL store boundary introduced in #1293. This is the address-manager follow-up stacked on #1295, so the combined branch exercises both complete manager surfaces behind one transaction boundary.

The goal here is deliberately narrow: carry the existing durable waddrmgr state across the backend boundary without exposing walletdb buckets to SQL callers. We don't route the wallet runtime through the new store or add migration logic in this PR.

Design

The new waddrmgr interfaces cover manager metadata, sync state, key scopes, accounts, addresses, and private-key deletion. The KV adapter binds its bucket once, then delegates to the existing waddrmgr helpers and row codecs. This keeps the Bolt path on the implementation we already use today.

For SQL, both SQLite and PostgreSQL use named sqlc queries and generated query types. There is no hand-written runtime SQL or DBTX escape hatch in the manager store. The dialect-specific query trees remain separate for now, with integer widths and nullable values normalized in the backend adapters.

The store preserves the details that matter for a port-first cut:

  • exact birthday block height, hash, and timestamp;
  • the existing callback transaction model;
  • legacy address identifiers and sticky used state;
  • both default and watch-only account encodings;
  • private-key deletion behavior;
  • legacy waddrmgr not-found error codes.

Relationship to the Existing waddrmgr

The new waddrmgr/store.go is the persistence contract and durable state model. The new waddrmgr/store_kv.go is the thin KV adapter over db.go. Neither file replaces manager.go or adds a second address manager.

manager.go remains the behavioral layer. It owns key management, locking, scopes, accounts, address derivation, and the other wallet operations already exposed by waddrmgr. db.go remains the existing KV persistence implementation. It owns the bucket layout, legacy rows, serialization, and low-level read/write helpers.

For the KV backend, store_kv.go binds the walletdb bucket once and presents the bucket-free interface. Its methods assemble the new durable state types or translate them back into the existing rows, then call the helpers in db.go. As an example, SyncState calls the existing start-block, synced-to, birthday, and birthday-block readers. PutAddress converts an AddressState into the existing address row and calls the existing putAddress helper.

The SQL backend implements the same interface in wallet/internal/db/sqlstore, using generated sqlc queries instead of the KV adapter. The intended routing is therefore:

manager.go
    |
    v
store interface
    |
    +-- KV binding in store_kv.go -> existing db.go helpers and codecs
    |
    +-- SQL store  -> generated sqlc queries

Keeping store_kv.go in the waddrmgr package is intentional: the adapter can reuse the existing unexported helpers, row types, and codecs without exporting KV internals or creating a second implementation. Production manager.go still calls walletdb and db.go directly today. Moving those call sites behind the interface is a later integration slice. This PR only creates the complete durable boundary and proves that the old KV representation and the two SQL backends can satisfy it.

The commits retain extraction trailers for the source work so the original contribution remains visible.

Testing

  • make sqlc
  • pinned golangci-lint v2.4.1 against the stack base, 0 issues
  • go test -count=1 ./waddrmgr ./wallet/internal/db/...
  • go test -tags=test_db_postgres -count=1 ./wallet/internal/db/itest -v
  • go vet ./waddrmgr ./wallet/internal/db/...

A full go test -count=1 ./... attempt passed the changed packages and stopped in chain/TestBitcoindEvents when the locally spawned bitcoind RPC endpoints refused connections.

yyforyongyu and others added 3 commits July 14, 2026 17:52
The SQL port needs a persistence boundary that does not expose a walletdb bucket on every operation. At the same time, the Bolt path should continue to use the existing row codecs and manager helpers.

In this commit, we add durable manager, sync, scope, account, and address state types together with read and write store interfaces. A thin KV adapter binds the bucket once and delegates to the existing implementation. The adapter preserves address hashes, used state, birthday block timestamps, and bulk sync restore semantics.

A focused KV test covers the adapter against a real manager database.

Extracted-from: 650062f

Extracted-from: 2839c1e
The manager boundary now covers the complete durable waddrmgr state, but the SQL backends still need to implement it without introducing a second hand-written query layer or changing wallet control flow.

In this commit, we add named sqlc queries for manager metadata, sync state, key scopes, accounts, and addresses. SQLite and PostgreSQL adapters normalize their generated integer and nullable types behind one store contract. The SQL store preserves the callback transaction model, legacy address identifiers, birthday block timestamps, private-key deletion behavior, and waddrmgr not-found errors.

The two dialect query trees remain explicit. All runtime SQL in this path is generated by sqlc.

Extracted-from: 168d1df470f95a04000181b43035cebb271b5cee

Extracted-from: 99e40ab

Extracted-from: 7e20ac1
The address-manager port needs one behavioral vector that runs unchanged against both SQL dialects. Separate happy-path tests would not catch differences in null handling, integer widths, or legacy error translation.

In this commit, we extend the manager-store conformance suite across the complete durable waddrmgr surface. The vector covers manager and sync metadata, exact birthday block stamps, scopes, both account encodings, address variants, used state, updates, private-key deletion, and legacy not-found errors.

The same test runs against SQLite and PostgreSQL.

Extracted-from: e0622ab

Extracted-from: f371c683950093a5306b57a9070d876ec32d956e
@Roasbeef
Roasbeef force-pushed the sql-port-stage2-waddrmgr branch from 3ca9326 to 3cf0329 Compare July 15, 2026 00:56
@Roasbeef
Roasbeef changed the base branch from sql-port-stage2-store-contract to sql-port-stage2-wtxmgr July 15, 2026 00:56
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.

3 participants