|
|
|
package btc
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|