Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 53 additions & 3 deletions cmd/chantools/fixoldbackup.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/lightninglabs/chantools/lnd"
"github.com/lightningnetwork/lnd/chanbackup"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain"
"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -77,15 +78,64 @@ func fixOldChannelBackup(multiFile *chanbackup.MultiFile,
for idx, single := range multi.StaticBackups {
err := ring.CheckDescriptor(single.ShaChainRootDesc)
switch {
case err == nil:
// If the descriptor is correct or the error is because no
// public key is provided, skip the channel. This can happen
// if the backup file has partially valid and invalid channels.
case err == nil || errors.Is(err, lnd.ErrNoPublicKeyProvided):
log.Infof("The shachain root for channel %v is "+
"correct, skipping...", single.FundingOutpoint)

continue

case errors.Is(err, keychain.ErrCannotDerivePrivKey):
// Before fixing the descriptor, print the funding
// multisig P2WSH witness program hash. Derive the
// local multisig pubkey via its locator if needed
// (and possible).
localPub, err := ring.PubKeyForDescriptor(
single.LocalChanCfg.MultiSigKey,
)
if err != nil {
return fmt.Errorf("could not derive local "+
"multisig pubkey for channel %v: %w",
single.FundingOutpoint, err)
}

remotePub, err := ring.PubKeyForDescriptor(
single.RemoteChanCfg.MultiSigKey,
)
if err != nil {
return fmt.Errorf("could not derive remote "+
"multisig pubkey for channel %v: %w",
single.FundingOutpoint, err)
}

a := localPub.SerializeCompressed()
b := remotePub.SerializeCompressed()
msScript, err := input.GenMultiSigScript(a, b)
if err != nil {
return fmt.Errorf("could not generate "+
"multisig script for channel "+
"%v: %w",
single.FundingOutpoint, err)
}

msHash, err := input.WitnessScriptHash(msScript)
if err != nil {
return fmt.Errorf("could not compute "+
"multisig script hash: %w", err)
}

log.Infof("Channel %v funding multisig P2WSH "+
"(pubkey): %x",
single.FundingOutpoint, msHash)

// Fix the incorrect descriptor by deriving a default
// one and overwriting it in the backup.
log.Infof("The shachain root for channel %s could "+
log.Infof("The shachain root for channel %v could "+
"not be derived, must be in old format. "+
"Fixing...", single.FundingOutpoint.String())
"Fixing...", single.FundingOutpoint)

baseKeyDesc, err := ring.DeriveKey(keychain.KeyLocator{
Family: keychain.KeyFamilyRevocationRoot,
Index: 0,
Expand Down
25 changes: 24 additions & 1 deletion lnd/hdkeychain.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ import (
"github.com/lightningnetwork/lnd/shachain"
)

// ErrNoPublicKeyProvided is returned when a key descriptor check is attempted
// without a public key present in the descriptor.
var ErrNoPublicKeyProvided = errors.New("no public key provided to check")

const (
HardenedKeyStart = uint32(hdkeychain.HardenedKeyStart)
WalletDefaultDerivationPath = "m/84'/0'/0'"
Expand Down Expand Up @@ -609,7 +613,7 @@ func (r *HDKeyRing) CheckDescriptor(

// A check doesn't make sense if there is no public key set.
if keyDesc.PubKey == nil {
return errors.New("no public key provided to check")
return ErrNoPublicKeyProvided
}

// Performance fix, derive static path only once.
Expand Down Expand Up @@ -658,3 +662,22 @@ func (r *HDKeyRing) NodePubKey() (*btcec.PublicKey, error) {

return keyDesc.PubKey, nil
}

// PubKeyForDescriptor returns the public key for a given key descriptor. If the
// descriptor already contains a public key, it is returned as-is. Otherwise,
// the public key is derived using the descriptor's key locator.
func (r *HDKeyRing) PubKeyForDescriptor(desc keychain.KeyDescriptor) (
*btcec.PublicKey, error) {

if desc.PubKey != nil {
return desc.PubKey, nil
}

// Attempt to derive using the provided key locator.
kd, err := r.DeriveKey(desc.KeyLocator)
if err != nil {
return nil, err
}

return kd.PubKey, nil
}