You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
chantools/lnd/hdkeychain.go

141 lines
3.3 KiB
Go

package lnd
import (
"fmt"
"strconv"
"strings"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcutil/hdkeychain"
"github.com/lightningnetwork/lnd/keychain"
)
const (
HardenedKeyStart = uint32(hdkeychain.HardenedKeyStart)
)
func DeriveChildren(key *hdkeychain.ExtendedKey, path []uint32) (
*hdkeychain.ExtendedKey, error) {
var (
currentKey = key
err error
)
for _, pathPart := range path {
currentKey, err = currentKey.Child(pathPart)
if err != nil {
return nil, err
}
}
return currentKey, nil
}
func ParsePath(path string) ([]uint32, error) {
path = strings.TrimSpace(path)
if len(path) == 0 {
return nil, fmt.Errorf("path cannot be empty")
}
if !strings.HasPrefix(path, "m/") {
return nil, fmt.Errorf("path must start with m/")
}
parts := strings.Split(path, "/")
indices := make([]uint32, len(parts)-1)
for i := 1; i < len(parts); i++ {
index := uint32(0)
part := parts[i]
if strings.Contains(parts[i], "'") {
index += HardenedKeyStart
part = strings.TrimRight(parts[i], "'")
}
parsed, err := strconv.Atoi(part)
if err != nil {
return nil, fmt.Errorf("could not parse part \"%s\": "+
"%v", part, err)
}
indices[i-1] = index + uint32(parsed)
}
return indices, nil
}
type HDKeyRing struct {
ExtendedKey *hdkeychain.ExtendedKey
ChainParams *chaincfg.Params
}
func (r *HDKeyRing) DeriveNextKey(_ keychain.KeyFamily) (
keychain.KeyDescriptor, error) {
return keychain.KeyDescriptor{}, nil
}
func (r *HDKeyRing) DeriveKey(keyLoc keychain.KeyLocator) (
keychain.KeyDescriptor, error) {
var empty = keychain.KeyDescriptor{}
derivedKey, err := DeriveChildren(r.ExtendedKey, []uint32{
HardenedKeyStart + uint32(keychain.BIP0043Purpose),
HardenedKeyStart + r.ChainParams.HDCoinType,
HardenedKeyStart + uint32(keyLoc.Family),
0,
keyLoc.Index,
})
if err != nil {
return empty, err
}
derivedPubKey, err := derivedKey.ECPubKey()
if err != nil {
return empty, err
}
return keychain.KeyDescriptor{
KeyLocator: keychain.KeyLocator{
Family: keyLoc.Family,
Index: keyLoc.Index,
},
PubKey: derivedPubKey,
}, nil
}
// Check if a key descriptor is correct by making sure that we can derive the
// key that it describes.
func (r *HDKeyRing) CheckDescriptor(
keyDesc keychain.KeyDescriptor) error {
// A check doesn't make sense if there is no public key set.
if keyDesc.PubKey == nil {
return fmt.Errorf("no public key provided to check")
}
// Performance fix, derive static path only once.
familyKey, err := DeriveChildren(r.ExtendedKey, []uint32{
HardenedKeyStart + uint32(keychain.BIP0043Purpose),
HardenedKeyStart + r.ChainParams.HDCoinType,
HardenedKeyStart + uint32(keyDesc.Family),
0,
})
if err != nil {
return err
}
// Scan the same key range as lnd would do on channel restore.
for i := 0; i < keychain.MaxKeyRangeScan; i++ {
child, err := DeriveChildren(familyKey, []uint32{uint32(i)})
if err != nil {
return err
}
pubKey, err := child.ECPubKey()
if err != nil {
return err
}
if !pubKey.IsEqual(keyDesc.PubKey) {
continue
}
// If we found the key, we can abort and signal success.
return nil
}
// We scanned the max range and didn't find a key. It's very likely not
// derivable with the given information.
return keychain.ErrCannotDerivePrivKey
}