lnwallet: update funding flow to utilize keychain.KeyRing

In this commit, we modify the funding flow process to obtain all keys
necessary from the keychain.KeyRing interface. This ensure that all
keys we generate are fully deterministic.
This commit is contained in:
Olaoluwa Osuntokun committed 2018-03-06 16:04:02 -05:00
1 parent fe12c908f8
commit a41f00e2d6
1 file changed
+53 -73
+53 -73
View File
@@ -10,6 +10,7 @@ import (
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/roasbeef/btcd/blockchain" "github.com/roasbeef/btcd/blockchain"
"github.com/roasbeef/btcd/chaincfg/chainhash" "github.com/roasbeef/btcd/chaincfg/chainhash"
@@ -27,24 +28,6 @@ const (
// The size of the buffered queue of requests to the wallet from the // The size of the buffered queue of requests to the wallet from the
// outside word. // outside word.
msgBufferSize = 100 msgBufferSize = 100
// revocationRootIndex is the top level HD key index from which secrets
// used to generate producer roots should be derived from.
revocationRootIndex = hdkeychain.HardenedKeyStart + 1
// identityKeyIndex is the top level HD key index which is used to
// generate/rotate identity keys.
//
// TODO(roasbeef): should instead be child to make room for future
// rotations, etc.
identityKeyIndex = hdkeychain.HardenedKeyStart + 2
)
var (
// Namespace bucket keys.
lightningNamespaceKey = []byte("ln-wallet")
waddrmgrNamespaceKey = []byte("waddrmgr")
wtxmgrNamespaceKey = []byte("wtxmgr")
) )
// ErrInsufficientFunds is a type matching the error interface which is // ErrInsufficientFunds is a type matching the error interface which is
@@ -248,6 +231,11 @@ type LightningWallet struct {
// specific interaction is proxied to the internal wallet. // specific interaction is proxied to the internal wallet.
WalletController WalletController
// SecretKeyRing is the interface we'll use to derive any keys related
// to our purpose within the network including: multi-sig keys, node
// keys, revocation keys, etc.
keychain.SecretKeyRing
// This mutex is to be held when generating external keys to be used as // This mutex is to be held when generating external keys to be used as
// multi-sig, and commitment keys within the channel. // multi-sig, and commitment keys within the channel.
keyGenMtx sync.RWMutex keyGenMtx sync.RWMutex
@@ -297,6 +285,7 @@ func NewLightningWallet(Cfg Config) (*LightningWallet, error) {
return &LightningWallet{ return &LightningWallet{
Cfg: Cfg, Cfg: Cfg,
SecretKeyRing: Cfg.SecretKeyRing,
WalletController: Cfg.WalletController, WalletController: Cfg.WalletController,
msgChan: make(chan interface{}, msgBufferSize), msgChan: make(chan interface{}, msgBufferSize),
nextFundingID: 0, nextFundingID: 0,
@@ -319,20 +308,6 @@ func (l *LightningWallet) Startup() error {
return err return err
} }
// Fetch the root derivation key from the wallet's HD chain. We'll use
// this to generate specific Lightning related secrets on the fly.
rootKey, err := l.FetchRootKey()
if err != nil {
return err
}
// TODO(roasbeef): always re-derive on the fly?
rootKeyRaw := rootKey.Serialize()
l.rootKey, err = hdkeychain.NewMaster(rootKeyRaw, &l.Cfg.NetParams)
if err != nil {
return err
}
l.wg.Add(1) l.wg.Add(1)
// TODO(roasbeef): multiple request handlers? // TODO(roasbeef): multiple request handlers?
go l.requestHandler() go l.requestHandler()
@@ -390,17 +365,6 @@ func (l *LightningWallet) ActiveReservations() []*ChannelReservation {
return reservations return reservations
} }
// GetIdentitykey returns the identity private key of the wallet.
// TODO(roasbeef): should be moved elsewhere
func (l *LightningWallet) GetIdentitykey() (*btcec.PrivateKey, error) {
identityKey, err := l.rootKey.Child(identityKeyIndex)
if err != nil {
return nil, err
}
return identityKey.ECPrivKey()
}
// requestHandler is the primary goroutine(s) responsible for handling, and // requestHandler is the primary goroutine(s) responsible for handling, and
// dispatching relies to all messages. // dispatching relies to all messages.
func (l *LightningWallet) requestHandler() { func (l *LightningWallet) requestHandler() {
@@ -534,35 +498,42 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg
// key, the base revocation key, the base htlc key,the base payment // key, the base revocation key, the base htlc key,the base payment
// key, and the delayed payment key. // key, and the delayed payment key.
// //
// TODO(roasbeef): special derivaiton? // TODO(roasbeef): "salt" each key as well?
reservation.ourContribution.MultiSigKey, err = l.NewRawKey() reservation.ourContribution.MultiSigKey, err = l.DeriveNextKey(
keychain.KeyFamilyMultiSig,
)
if err != nil { if err != nil {
req.err <- err req.err <- err
req.resp <- nil req.resp <- nil
return return
} }
reservation.ourContribution.RevocationBasePoint, err = l.NewRawKey() reservation.ourContribution.RevocationBasePoint, err = l.DeriveNextKey(
keychain.KeyFamilyRevocationBase,
)
if err != nil { if err != nil {
req.err <- err req.err <- err
req.resp <- nil req.resp <- nil
return return
} }
reservation.ourContribution.HtlcBasePoint, err = l.NewRawKey() reservation.ourContribution.HtlcBasePoint, err = l.DeriveNextKey(
keychain.KeyFamilyHtlcBase,
)
if err != nil { if err != nil {
req.err <- err req.err <- err
req.resp <- nil req.resp <- nil
return return
} }
// TODO(roasbeef); allow for querying to extract key distinct from HD reservation.ourContribution.PaymentBasePoint, err = l.DeriveNextKey(
// chain keychain.KeyFamilyPaymentBase,
// * allows for offline commitment keys )
reservation.ourContribution.PaymentBasePoint, err = l.NewRawKey()
if err != nil { if err != nil {
req.err <- err req.err <- err
req.resp <- nil req.resp <- nil
return return
} }
reservation.ourContribution.DelayBasePoint, err = l.NewRawKey() reservation.ourContribution.DelayBasePoint, err = l.DeriveNextKey(
keychain.KeyFamilyDelayBase,
)
if err != nil { if err != nil {
req.err <- err req.err <- err
req.resp <- nil req.resp <- nil
@@ -750,8 +721,10 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) {
// Finally, add the 2-of-2 multi-sig output which will set up the lightning // Finally, add the 2-of-2 multi-sig output which will set up the lightning
// channel. // channel.
channelCapacity := int64(pendingReservation.partialState.Capacity) channelCapacity := int64(pendingReservation.partialState.Capacity)
witnessScript, multiSigOut, err := GenFundingPkScript(ourKey.SerializeCompressed(), witnessScript, multiSigOut, err := GenFundingPkScript(
theirKey.SerializeCompressed(), channelCapacity) ourKey.PubKey.SerializeCompressed(),
theirKey.PubKey.SerializeCompressed(), channelCapacity,
)
if err != nil { if err != nil {
req.err <- err req.err <- err
return return
@@ -850,22 +823,22 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) {
var stateObfuscator [StateHintSize]byte var stateObfuscator [StateHintSize]byte
if chanState.ChanType == channeldb.SingleFunder { if chanState.ChanType == channeldb.SingleFunder {
stateObfuscator = DeriveStateHintObfuscator( stateObfuscator = DeriveStateHintObfuscator(
ourContribution.PaymentBasePoint, ourContribution.PaymentBasePoint.PubKey,
theirContribution.PaymentBasePoint, theirContribution.PaymentBasePoint.PubKey,
) )
} else { } else {
ourSer := ourContribution.PaymentBasePoint.SerializeCompressed() ourSer := ourContribution.PaymentBasePoint.PubKey.SerializeCompressed()
theirSer := theirContribution.PaymentBasePoint.SerializeCompressed() theirSer := theirContribution.PaymentBasePoint.PubKey.SerializeCompressed()
switch bytes.Compare(ourSer, theirSer) { switch bytes.Compare(ourSer, theirSer) {
case -1: case -1:
stateObfuscator = DeriveStateHintObfuscator( stateObfuscator = DeriveStateHintObfuscator(
ourContribution.PaymentBasePoint, ourContribution.PaymentBasePoint.PubKey,
theirContribution.PaymentBasePoint, theirContribution.PaymentBasePoint.PubKey,
) )
default: default:
stateObfuscator = DeriveStateHintObfuscator( stateObfuscator = DeriveStateHintObfuscator(
theirContribution.PaymentBasePoint, theirContribution.PaymentBasePoint.PubKey,
ourContribution.PaymentBasePoint, ourContribution.PaymentBasePoint.PubKey,
) )
} }
} }
@@ -895,7 +868,7 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) {
// transaction. // transaction.
signDesc = SignDescriptor{ signDesc = SignDescriptor{
WitnessScript: witnessScript, WitnessScript: witnessScript,
PubKey: ourKey, KeyDesc: ourKey,
Output: multiSigOut, Output: multiSigOut,
HashType: txscript.SigHashAll, HashType: txscript.SigHashAll,
SigHashes: txscript.NewTxSigHashes(theirCommitTx), SigHashes: txscript.NewTxSigHashes(theirCommitTx),
@@ -1038,8 +1011,11 @@ func (l *LightningWallet) handleFundingCounterPartySigs(msg *addCounterPartySigs
// Re-generate both the witnessScript and p2sh output. We sign the // Re-generate both the witnessScript and p2sh output. We sign the
// witnessScript script, but include the p2sh output as the subscript // witnessScript script, but include the p2sh output as the subscript
// for verification. // for verification.
witnessScript, _, err := GenFundingPkScript(ourKey.SerializeCompressed(), witnessScript, _, err := GenFundingPkScript(
theirKey.SerializeCompressed(), int64(res.partialState.Capacity)) ourKey.PubKey.SerializeCompressed(),
theirKey.PubKey.SerializeCompressed(),
int64(res.partialState.Capacity),
)
if err != nil { if err != nil {
msg.err <- err msg.err <- err
msg.completeChan <- nil msg.completeChan <- nil
@@ -1066,7 +1042,7 @@ func (l *LightningWallet) handleFundingCounterPartySigs(msg *addCounterPartySigs
msg.err <- err msg.err <- err
msg.completeChan <- nil msg.completeChan <- nil
return return
} else if !sig.Verify(sigHash, theirKey) { } else if !sig.Verify(sigHash, theirKey.PubKey) {
msg.err <- fmt.Errorf("counterparty's commitment signature is invalid") msg.err <- fmt.Errorf("counterparty's commitment signature is invalid")
msg.completeChan <- nil msg.completeChan <- nil
return return
@@ -1168,8 +1144,9 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
// generator state obfuscator to encode the current state number within // generator state obfuscator to encode the current state number within
// both commitment transactions. // both commitment transactions.
stateObfuscator := DeriveStateHintObfuscator( stateObfuscator := DeriveStateHintObfuscator(
pendingReservation.theirContribution.PaymentBasePoint, pendingReservation.theirContribution.PaymentBasePoint.PubKey,
pendingReservation.ourContribution.PaymentBasePoint) pendingReservation.ourContribution.PaymentBasePoint.PubKey,
)
err = initStateHints(ourCommitTx, theirCommitTx, stateObfuscator) err = initStateHints(ourCommitTx, theirCommitTx, stateObfuscator)
if err != nil { if err != nil {
req.err <- err req.err <- err
@@ -1194,8 +1171,10 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
hashCache := txscript.NewTxSigHashes(ourCommitTx) hashCache := txscript.NewTxSigHashes(ourCommitTx)
theirKey := pendingReservation.theirContribution.MultiSigKey theirKey := pendingReservation.theirContribution.MultiSigKey
ourKey := pendingReservation.ourContribution.MultiSigKey ourKey := pendingReservation.ourContribution.MultiSigKey
witnessScript, _, err := GenFundingPkScript(ourKey.SerializeCompressed(), witnessScript, _, err := GenFundingPkScript(
theirKey.SerializeCompressed(), channelValue) ourKey.PubKey.SerializeCompressed(),
theirKey.PubKey.SerializeCompressed(), channelValue,
)
if err != nil { if err != nil {
req.err <- err req.err <- err
req.completeChan <- nil req.completeChan <- nil
@@ -1217,8 +1196,9 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
req.err <- err req.err <- err
req.completeChan <- nil req.completeChan <- nil
return return
} else if !sig.Verify(sigHash, theirKey) { } else if !sig.Verify(sigHash, theirKey.PubKey) {
req.err <- fmt.Errorf("counterparty's commitment signature is invalid") req.err <- fmt.Errorf("counterparty's commitment signature " +
"is invalid")
req.completeChan <- nil req.completeChan <- nil
return return
} }
@@ -1235,7 +1215,7 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
} }
signDesc := SignDescriptor{ signDesc := SignDescriptor{
WitnessScript: witnessScript, WitnessScript: witnessScript,
PubKey: ourKey, KeyDesc: ourKey,
Output: &wire.TxOut{ Output: &wire.TxOut{
PkScript: p2wsh, PkScript: p2wsh,
Value: channelValue, Value: channelValue,