keychain+walletrpc: add DeriveAndStoreKey - #11095
Conversation
DeriveNextKey records the key it derives in the wallet's address manager, while DeriveKey does not. That makes the two methods behave differently in a way that matters: since the wallet has no record of a key derived through DeriveKey, it cannot map that key's public key back to its key locator, and that mapping is what the wallet needs in order to sign with the key. Keys derived at an arbitrary index are therefore not usable for signing, and they show up in neither ListAccounts nor ListAddresses. This adds DeriveAndStoreKey, the variant of DeriveKey that records the key it derives. Recording a key implies recording every key in the family that precedes it, so the call doubles as a way to advance a key family's derivation index: an external consumer of a key family that tracks its own indexes can restore the wallet's index after the wallet was recovered from seed, instead of calling DeriveNextKey in a loop until it reaches the index it needs. The call is monotonic and idempotent, a family is never rewound, and the number of keys a single call derives is bounded by MaxKeyIndexExtension so that a request for a far away index is refused instead of filling the address manager.
Exposes keychain's DeriveAndStoreKey over the WalletKit service, so RPC consumers of a key family can derive a key that the wallet is able to sign with later on, and can restore the family's derivation index after the wallet was recovered from seed. The macaroon permission matches the one DeriveKey and DeriveNextKey use.
Asserts that a key derived without storing it leaves the key family's derivation index alone and stays unknown to the wallet, that storing one advances the index past it and makes it known, that DeriveNextKey does not hand out a consumed index again, that storing an index the family already passed does not rewind it, that an index too far ahead is refused instead of deriving an unbounded number of keys, and that negative locators are rejected. The same assertions run against a watch-only node driving a remote signer, which additionally covers the case the new method exists for: signing with a key that is referenced by its public key alone requires the wallet to resolve the key's locator through its address, so it fails for a key that was only derived and succeeds once the key is stored. That resolution is skipped on a node with a local wallet, which falls back to scanning a range of key indexes, so only the remote signer exercises it.
7ea238b to
5b9f177
Compare
The wrapper added in the previous commit calls an RPC that does not exist in any released LND yet, so this points the LND dependency at the branch of lightningnetwork/lnd#11095 to make the build work in the meantime. This commit must be dropped in favor of a normal LND version bump before this branch can be merged.
🔴 PR Severity: CRITICAL
🔴 Critical (4 files)
🟠 High (7 files)
🟡 Medium (2 files)
🟢 Low (7 files)
AnalysisThis PR modifies To override, add a |
Recording a key in LND implies recording every key that precedes it, so restoring a key family's index is one DeriveAndStoreKey call per family instead of one DeriveNextKey per index. LND keeps that operation monotonic and bounds the number of keys it derives, so the local advance loop, the rewind guard and the maxKeyIndexAdvance limit are no longer needed here. Requires an LND with the DeriveAndStoreKey RPC (lightningnetwork/lnd#11095) and the lndclient wrapper for it (lightninglabs/lndclient#288).
Recording a key in LND implies recording every key that precedes it, so restoring a key family's index is one DeriveAndStoreKey call per family instead of one DeriveNextKey per index. LND keeps that operation monotonic and bounds the number of keys it derives, so the local advance loop, the rewind guard and the maxKeyIndexAdvance limit are no longer needed here. Requires an LND with the DeriveAndStoreKey RPC (lightningnetwork/lnd#11095) and the lndclient wrapper for it (lightninglabs/lndclient#288).
Recording a key in LND implies recording every key that precedes it, so restoring a key family's index is one DeriveAndStoreKey call per family instead of one DeriveNextKey per index. LND keeps that operation monotonic and bounds the number of keys it derives, so the local advance loop, the rewind guard and the maxKeyIndexAdvance limit are no longer needed here. Requires an LND with the DeriveAndStoreKey RPC (lightningnetwork/lnd#11095) and the lndclient wrapper for it (lightninglabs/lndclient#288).
Recording a key in LND implies recording every key that precedes it, so restoring a key family's index is one DeriveAndStoreKey call per family instead of one DeriveNextKey per index. LND keeps that operation monotonic and bounds the number of keys it derives, so the local advance loop, the rewind guard and the maxKeyIndexAdvance limit are no longer needed here. Requires an LND with the DeriveAndStoreKey RPC (lightningnetwork/lnd#11095) and the lndclient wrapper for it (lightninglabs/lndclient#288).
The wrapper added in the previous commit calls an RPC that does not exist in any released LND yet, so this points the LND dependency at the branch of lightningnetwork/lnd#11095 to make the build work in the meantime. This commit must be dropped in favor of a normal LND version bump before this branch can be merged.
|
@hieblmi: review reminder |
Description
DeriveNextKeyrecords the key it derives in the wallet,DeriveKeydoes not. So a key derived at an arbitrary index is invisible toListAccountsand cannot be signed with, and an external consumer of a key family has no way to tell the wallet how far that family has been used, which matters after a seed-only recovery.This adds
DeriveAndStoreKey, the variant ofDeriveKeythat records the key, onkeychain.KeyRingand as awalletrpcRPC. Recording a key implies recording every key before it, so it also advances the family's index in one call. Monotonic, idempotent, never rewinds, and bounded byMaxKeyIndexExtension.DeriveKeyis left as is on purpose, since its callers pass indexes from channel backups and the remote signer signing path.Related PRs / issues
Consumer: lightninglabs/taproot-assets#2225. Verified end to end against that branch with a matching
lndclientwrapper, tapd's seed restore itest passing on sqlite and postgres.Addresses #8698.