mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-06-25 16:23:49 +02:00
Merge pull request #2368 from Roasbeef/static-chan-backups-keychain
keychain: extend DerivePrivKey to derive based on pubkey+KeyFamily
This commit is contained in:
commit
3c950e8f0d
@ -269,8 +269,12 @@ func (b *BtcWalletKeyRing) DerivePrivKey(keyDesc KeyDescriptor) (*btcec.PrivateK
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now that we know the account exists, we can safely derive
|
// If the public key isn't set or they have a non-zero index,
|
||||||
// the full private key from the given path.
|
// then we know that the caller instead knows the derivation
|
||||||
|
// path for a key.
|
||||||
|
if keyDesc.PubKey == nil || keyDesc.Index > 0 {
|
||||||
|
// Now that we know the account exists, we can safely
|
||||||
|
// derive the full private key from the given path.
|
||||||
path := waddrmgr.DerivationPath{
|
path := waddrmgr.DerivationPath{
|
||||||
Account: uint32(keyDesc.Family),
|
Account: uint32(keyDesc.Family),
|
||||||
Branch: 0,
|
Branch: 0,
|
||||||
@ -287,6 +291,52 @@ func (b *BtcWalletKeyRing) DerivePrivKey(keyDesc KeyDescriptor) (*btcec.PrivateK
|
|||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the public key isn't nil, then this indicates that we
|
||||||
|
// need to scan for the private key, assuming that we know the
|
||||||
|
// valid key family.
|
||||||
|
nextPath := waddrmgr.DerivationPath{
|
||||||
|
Account: uint32(keyDesc.Family),
|
||||||
|
Branch: 0,
|
||||||
|
Index: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll now iterate through our key range in an attempt to
|
||||||
|
// find the target public key.
|
||||||
|
//
|
||||||
|
// TODO(roasbeef): possibly move scanning into wallet to allow
|
||||||
|
// to be parallelized
|
||||||
|
for i := 0; i < MaxKeyRangeScan; i++ {
|
||||||
|
// Derive the next key in the range and fetch its
|
||||||
|
// managed address.
|
||||||
|
addr, err := scope.DeriveFromKeyPath(
|
||||||
|
addrmgrNs, nextPath,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
managedAddr := addr.(waddrmgr.ManagedPubKeyAddress)
|
||||||
|
|
||||||
|
// If this is the target public key, then we'll return
|
||||||
|
// it directly back to the caller.
|
||||||
|
if managedAddr.PubKey().IsEqual(keyDesc.PubKey) {
|
||||||
|
key, err = managedAddr.PrivKey()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This wasn't the target key, so roll forward and try
|
||||||
|
// the next one.
|
||||||
|
nextPath.Index++
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we reach this point, then we we're unable to derive the
|
||||||
|
// private key, so return an error back to the user.
|
||||||
|
return ErrCannotDerivePrivKey
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
package keychain
|
package keychain
|
||||||
|
|
||||||
import "github.com/btcsuite/btcd/btcec"
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/btcec"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// KeyDerivationVersion is the version of the key derivation schema
|
// KeyDerivationVersion is the version of the key derivation schema
|
||||||
@ -20,6 +24,18 @@ const (
|
|||||||
BIP0043Purpose = 1017
|
BIP0043Purpose = 1017
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// MaxKeyRangeScan is the maximum number of keys that we'll attempt to
|
||||||
|
// scan with if a caller knows the public key, but not the KeyLocator
|
||||||
|
// and wishes to derive a private key.
|
||||||
|
MaxKeyRangeScan = 100000
|
||||||
|
|
||||||
|
// ErrCannotDerivePrivKey is returned when DerivePrivKey is unable to
|
||||||
|
// derive a private key given only the public key and target key
|
||||||
|
// family.
|
||||||
|
ErrCannotDerivePrivKey = fmt.Errorf("unable to derive private key")
|
||||||
|
)
|
||||||
|
|
||||||
// KeyFamily represents a "family" of keys that will be used within various
|
// KeyFamily represents a "family" of keys that will be used within various
|
||||||
// contracts created by lnd. These families are meant to be distinct branches
|
// contracts created by lnd. These families are meant to be distinct branches
|
||||||
// within the HD key chain of the backing wallet. Usage of key families within
|
// within the HD key chain of the backing wallet. Usage of key families within
|
||||||
@ -141,7 +157,10 @@ type SecretKeyRing interface {
|
|||||||
KeyRing
|
KeyRing
|
||||||
|
|
||||||
// DerivePrivKey attempts to derive the private key that corresponds to
|
// DerivePrivKey attempts to derive the private key that corresponds to
|
||||||
// the passed key descriptor.
|
// the passed key descriptor. If the public key is set, then this
|
||||||
|
// method will perform an in-order scan over the key set, with a max of
|
||||||
|
// MaxKeyRangeScan keys. In order for this to work, the caller MUST set
|
||||||
|
// the KeyFamily within the partially populated KeyLocator.
|
||||||
DerivePrivKey(keyDesc KeyDescriptor) (*btcec.PrivateKey, error)
|
DerivePrivKey(keyDesc KeyDescriptor) (*btcec.PrivateKey, error)
|
||||||
|
|
||||||
// ScalarMult performs a scalar multiplication (ECDH-like operation)
|
// ScalarMult performs a scalar multiplication (ECDH-like operation)
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/btcec"
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/btcsuite/btcd/chaincfg"
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||||
@ -316,7 +317,7 @@ func TestSecretKeyRingDerivation(t *testing.T) {
|
|||||||
defer cleanUp()
|
defer cleanUp()
|
||||||
|
|
||||||
success := t.Run(fmt.Sprintf("%v", keyRingName), func(t *testing.T) {
|
success := t.Run(fmt.Sprintf("%v", keyRingName), func(t *testing.T) {
|
||||||
// First, each key family, we'll ensure that we're able
|
// For, each key family, we'll ensure that we're able
|
||||||
// to obtain the private key of a randomly select child
|
// to obtain the private key of a randomly select child
|
||||||
// index within the key family.
|
// index within the key family.
|
||||||
for _, keyFam := range versionZeroKeyFamilies {
|
for _, keyFam := range versionZeroKeyFamilies {
|
||||||
@ -356,6 +357,57 @@ func TestSecretKeyRingDerivation(t *testing.T) {
|
|||||||
privKey.PubKey().SerializeCompressed())
|
privKey.PubKey().SerializeCompressed())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Next, we'll test that we're able to derive a
|
||||||
|
// key given only the public key and key
|
||||||
|
// family.
|
||||||
|
//
|
||||||
|
// Derive a new key from the key ring.
|
||||||
|
keyDesc, err := secretKeyRing.DeriveNextKey(keyFam)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to derive key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll now construct a key descriptor that
|
||||||
|
// requires us to scan the key range, and query
|
||||||
|
// for the key, we should be able to find it as
|
||||||
|
// it's valid.
|
||||||
|
keyDesc = KeyDescriptor{
|
||||||
|
PubKey: keyDesc.PubKey,
|
||||||
|
KeyLocator: KeyLocator{
|
||||||
|
Family: keyFam,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
privKey, err = secretKeyRing.DerivePrivKey(keyDesc)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to derive priv key "+
|
||||||
|
"via scanning: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Having to resort to scanning, we should be
|
||||||
|
// able to find the target public key.
|
||||||
|
if !keyDesc.PubKey.IsEqual(privKey.PubKey()) {
|
||||||
|
t.Fatalf("pubkeys mismatched: expected %x, got %x",
|
||||||
|
pubKeyDesc.PubKey.SerializeCompressed(),
|
||||||
|
privKey.PubKey().SerializeCompressed())
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll try again, but this time with an
|
||||||
|
// unknown public key.
|
||||||
|
_, pub := btcec.PrivKeyFromBytes(
|
||||||
|
btcec.S256(), testHDSeed[:],
|
||||||
|
)
|
||||||
|
keyDesc.PubKey = pub
|
||||||
|
|
||||||
|
// If we attempt to query for this key, then we
|
||||||
|
// should get ErrCannotDerivePrivKey.
|
||||||
|
privKey, err = secretKeyRing.DerivePrivKey(
|
||||||
|
keyDesc,
|
||||||
|
)
|
||||||
|
if err != ErrCannotDerivePrivKey {
|
||||||
|
t.Fatalf("expected %T, instead got %v",
|
||||||
|
ErrCannotDerivePrivKey, err)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(roasbeef): scalar mult once integrated
|
// TODO(roasbeef): scalar mult once integrated
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -364,3 +416,9 @@ func TestSecretKeyRingDerivation(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// We'll clamp the max range scan to constrain the run time of the
|
||||||
|
// private key scan test.
|
||||||
|
MaxKeyRangeScan = 3
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user