Skip to content

wallet+db: add GetAccountSecret Store surface and keyvault routing#1272

Draft
yyforyongyu wants to merge 11 commits into
impl-runtime-store-5from
impl-secret-store
Draft

wallet+db: add GetAccountSecret Store surface and keyvault routing#1272
yyforyongyu wants to merge 11 commits into
impl-runtime-store-5from
impl-secret-store

Conversation

@yyforyongyu

Copy link
Copy Markdown
Collaborator

Adds the account-secret surface to the runtime db.Store: the GetAccountSecret
query, secret types, sqlite/pg implementations, and the store-contract method.
Also adds the legacy manager keyvault shim and routes wallet unlock/lock and
signer key derivation through the keyvault (store-backed derivation primitives,
with a legacy-miss fallback).

Split out of the secret Store PR for reviewability; content-preserving, no
commit changes. Stacked on impl-runtime-store-5.

@yyforyongyu yyforyongyu added this to the Introduce SQL store milestone Jun 17, 2026
@yyforyongyu yyforyongyu self-assigned this Jun 17, 2026
@coveralls

coveralls commented Jun 17, 2026

Copy link
Copy Markdown

Coverage Report for CI Build 28320808284

Warning

No base build found for commit 3214fb8 on impl-runtime-store-5.
Coverage changes can't be calculated without a base build.
If a base build is processing, this comment will update automatically when it completes.

Coverage: 56.298%

Details

  • Patch coverage: 234 uncovered changes across 12 files (208 of 442 lines covered, 47.06%).

Uncovered Changes

Top 10 Files by Coverage Impact Changed Covered %
wallet/signer.go 139 61 43.88%
wallet/internal/db/kvdb/vault.go 54 7 12.96%
wallet/internal/sql/pg/sqlc/accounts.sql.go 21 0 0.0%
wallet/internal/sql/sqlite/sqlc/accounts.sql.go 21 0 0.0%
wallet/internal/db/pg/accountstore_getaccountsecret.go 76 58 76.32%
wallet/internal/db/sqlite/accountstore_getaccountsecret.go 76 58 76.32%
wallet/internal/sql/pg/sqlc/db.go 8 0 0.0%
wallet/internal/db/kvdb/accountstore_getaccountsecret.go 7 0 0.0%
wallet/internal/sql/sqlite/sqlc/db.go 8 1 12.5%
wallet/internal/db/accountstore_getaccountsecret.go 8 4 50.0%
Total (15 files) 442 208 47.06%

Coverage Regressions

Requires a base build to compare against. How to fix this →


Coverage Stats

Coverage Status
Relevant Lines: 59809
Covered Lines: 33671
Line Coverage: 56.3%
Coverage Strength: 11335.75 hits per line

💛 - Coveralls

@yyforyongyu yyforyongyu force-pushed the impl-runtime-store-5 branch from 449c243 to e919fcb Compare June 17, 2026 12:22
@saubyk saubyk added this to lnd v0.22 Jun 17, 2026
@github-project-automation github-project-automation Bot moved this to Backlog in lnd v0.22 Jun 17, 2026
@yyforyongyu yyforyongyu marked this pull request as draft June 17, 2026 15:23
@yyforyongyu

Copy link
Copy Markdown
Collaborator Author

cc @GustavoStingelin for taking over, I think we can open a new PR instead, as this PR is only a draft.

@yyforyongyu yyforyongyu force-pushed the impl-runtime-store-5 branch from e919fcb to e8d5e1a Compare June 17, 2026 16:06
@yyforyongyu yyforyongyu force-pushed the impl-secret-store branch 2 times, most recently from c70b257 to f572dca Compare June 17, 2026 16:25
@yyforyongyu yyforyongyu force-pushed the impl-runtime-store-5 branch 2 times, most recently from b7b60d7 to a354b6c Compare June 17, 2026 16:42
@yyforyongyu yyforyongyu force-pushed the impl-runtime-store-5 branch from a354b6c to 0908fc0 Compare June 17, 2026 17:32
@yyforyongyu yyforyongyu force-pushed the impl-runtime-store-5 branch from 0908fc0 to 91960b1 Compare June 17, 2026 19:05
@yyforyongyu yyforyongyu force-pushed the impl-runtime-store-5 branch from 91960b1 to edba65b Compare June 17, 2026 19:17
@yyforyongyu yyforyongyu force-pushed the impl-secret-store branch 2 times, most recently from 4b277a5 to de9ddb6 Compare June 18, 2026 06:15
@yyforyongyu yyforyongyu force-pushed the impl-runtime-store-5 branch 8 times, most recently from a5924bd to edea01b Compare June 18, 2026 18:34
@yyforyongyu yyforyongyu force-pushed the impl-runtime-store-5 branch 13 times, most recently from 2e6f6b4 to ba9f44a Compare June 27, 2026 12:51
@yyforyongyu yyforyongyu force-pushed the impl-runtime-store-5 branch from ba9f44a to 3fe8cf7 Compare June 27, 2026 20:53
@yyforyongyu yyforyongyu force-pushed the impl-runtime-store-5 branch from 3fe8cf7 to 180f71b Compare June 27, 2026 21:15
@yyforyongyu yyforyongyu force-pushed the impl-runtime-store-5 branch from 180f71b to e21896f Compare June 27, 2026 21:36
Add LegacyManagerVault wrapping walletdb.DB and *waddrmgr.Manager,
implementing keyvault.Vault by delegating to the legacy address manager.
Unlock authenticates the private passphrase inside a walletdb view and
ignores the auto-lock timeout, since the controller keeps owning that
timer. Lock is void and swallows the idempotent ErrLocked, logging any
other failure. IsLocked, Encrypt, and Decrypt forward to the manager, and
RefreshPrivatePassphrase is a no-op because the manager rotates its crypto
state in place.

The shim lives beside the other kvdb legacy adapters and lets later commits
route wallet auth and key-material encryption through the vault seam without
changing behavior.

(cherry picked from commit fec2904)
Add the GetAccountSecret query to the pg and sqlite account query sets
and regenerate the sqlc bindings. The query joins accounts to key_scopes
and left-joins account_secrets so callers can distinguish a watch-only
account (no secret row) from an absent account. Generated code is kept
in its own commit, separate from the hand-written Go that consumes it.

(cherry picked from commit 99c5994)
Add the AccountSecret result type and the GetAccountSecretQuery selector,
plus the GetAccountSecretQuery.Validate helper and the
ErrAccountSecretUnavailable sentinel. AccountSecret carries only encrypted
private-key material; decryption stays with the wallet key vault. These
types back the per-backend GetAccountSecret implementations that follow.

(cherry picked from commit dc242ba)
Add the PostgreSQL Store.GetAccountSecret method backed by the generated
GetAccountSecret query. It validates the selector, maps the row to the
backend-independent AccountSecret, and reports a typed ErrAccountNotFound
when no account row matches. The method is not yet part of the AccountStore
interface; that wiring lands once every backend implements it.

(cherry picked from commit 69c0b75)
Add the SQLite Store.GetAccountSecret method backed by the generated
GetAccountSecret query, mirroring the PostgreSQL implementation: validate
the selector, map the row to AccountSecret, and surface ErrAccountNotFound
for a missing account. Interface wiring lands once all backends implement
it.

(cherry picked from commit cf0865f)
Add GetAccountSecret to the AccountStore interface now that pg and sqlite
implement it, and complete the remaining implementations: the kvdb backend
reports ErrAccountSecretUnavailable (legacy secrets stay in waddrmgr), and
the testify mock store gains the method. The runtime cache exposes a
pass-through GetAccountSecret seam, and an itest covers derived, watch-only,
and absent-account outcomes.

(cherry picked from commit 21ce20d)
Build the keyVault from kvdb.NewLegacyManagerVault during Load and
OpenWithRetry instead of the not-yet-implemented keyvault.NewDBVault stub.
The kvdb-open path now flows wallet auth and key-material encryption through
a real legacy-backed vault while behavior stays unchanged. The SQL-open path
keeps the DBVault stub until its runtime crypto lands.

(cherry picked from commit 2d073a5)
Route handleUnlockReq and handleLockReq through the key vault instead of
the wallet-owned DBUnlock helper, and delete DBUnlock and its test. Unlock
passes a negative timeout so the controller keeps owning its auto-lock
timer; Lock is void, with the idempotent ErrLocked swallow now living in
the vault.

The vault interface has no passphrase-change method, so the passphrase
rotation stays in the wallet: handleChangePassphraseReq persists the change
through DBPutPassphrase and then asks the vault to refresh its runtime state
on a private rotation. The controller tests assert against the vault and the
restored DBPutPassphrase path.

(cherry picked from commit da88730)
Add deriveStoredAccountChildKey, which decrypts an account's encrypted
private key through the key vault and derives the branch/index leaf key
while zeroing intermediate material, and isWaddrmgrAccountClassError for
classifying waddrmgr scope/account misses. Also add the ErrWatchOnlyAccount
and ErrAccountNotInStore sentinels. These primitives back the store-backed
signer fallback wired in next.

(cherry picked from commit be8bb18)
Add derivePathPrivKey and resolveDerivedPrivKeyFromStore, and thread a
context through the signing paths (ECDH, SignDigest, ComputeRawSig,
DerivePrivKey and the output/imported-key helpers). When the legacy
waddrmgr lookup misses on a scope or account, resolve the account's
encrypted private key from the Store and derive the leaf key through the
key vault, with precise watch-only and not-in-store errors. Covers
SQL-only accounts that have no mirrored waddrmgr row.

(cherry picked from commit de9ddb6)
…BT account

#13: mirror deprecated public-key + taproot-script imports into w.store for SQL
(skip the redundant kvdb mirror, where the legacy manager is the store).
#14: DerivePubKey falls back to the account xpub from the store for SQL-only
accounts (resolveDerivedPubKeyFromStore), matching the privkey fallback.
#15: parseBip32Path sets InternalAccount alongside Account so non-zero-account
PSBT signing resolves the correct account secret instead of account 0.

(cherry picked from commit ca4c9d4)

# Conflicts:
#	wallet/signer_test.go
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