From a41f00e2d63c075d0f008b804ecf918ebfe504c0 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sat, 17 Feb 2018 15:20:41 -0800 Subject: [PATCH] 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. --- lnwallet/wallet.go | 126 +++++++++++++++++++-------------------------- 1 file changed, 53 insertions(+), 73 deletions(-) diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index c394ff2d2..563c6a591 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -10,6 +10,7 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwire" "github.com/roasbeef/btcd/blockchain" "github.com/roasbeef/btcd/chaincfg/chainhash" @@ -27,24 +28,6 @@ const ( // The size of the buffered queue of requests to the wallet from the // outside word. 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 @@ -248,6 +231,11 @@ type LightningWallet struct { // specific interaction is proxied to the internal wallet. 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 // multi-sig, and commitment keys within the channel. keyGenMtx sync.RWMutex @@ -297,6 +285,7 @@ func NewLightningWallet(Cfg Config) (*LightningWallet, error) { return &LightningWallet{ Cfg: Cfg, + SecretKeyRing: Cfg.SecretKeyRing, WalletController: Cfg.WalletController, msgChan: make(chan interface{}, msgBufferSize), nextFundingID: 0, @@ -319,20 +308,6 @@ func (l *LightningWallet) Startup() error { 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) // TODO(roasbeef): multiple request handlers? go l.requestHandler() @@ -390,17 +365,6 @@ func (l *LightningWallet) ActiveReservations() []*ChannelReservation { 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 // dispatching relies to all messages. 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, and the delayed payment key. // - // TODO(roasbeef): special derivaiton? - reservation.ourContribution.MultiSigKey, err = l.NewRawKey() + // TODO(roasbeef): "salt" each key as well? + reservation.ourContribution.MultiSigKey, err = l.DeriveNextKey( + keychain.KeyFamilyMultiSig, + ) if err != nil { req.err <- err req.resp <- nil return } - reservation.ourContribution.RevocationBasePoint, err = l.NewRawKey() + reservation.ourContribution.RevocationBasePoint, err = l.DeriveNextKey( + keychain.KeyFamilyRevocationBase, + ) if err != nil { req.err <- err req.resp <- nil return } - reservation.ourContribution.HtlcBasePoint, err = l.NewRawKey() + reservation.ourContribution.HtlcBasePoint, err = l.DeriveNextKey( + keychain.KeyFamilyHtlcBase, + ) if err != nil { req.err <- err req.resp <- nil return } - // TODO(roasbeef); allow for querying to extract key distinct from HD - // chain - // * allows for offline commitment keys - reservation.ourContribution.PaymentBasePoint, err = l.NewRawKey() + reservation.ourContribution.PaymentBasePoint, err = l.DeriveNextKey( + keychain.KeyFamilyPaymentBase, + ) if err != nil { req.err <- err req.resp <- nil return } - reservation.ourContribution.DelayBasePoint, err = l.NewRawKey() + reservation.ourContribution.DelayBasePoint, err = l.DeriveNextKey( + keychain.KeyFamilyDelayBase, + ) if err != nil { req.err <- err 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 // channel. channelCapacity := int64(pendingReservation.partialState.Capacity) - witnessScript, multiSigOut, err := GenFundingPkScript(ourKey.SerializeCompressed(), - theirKey.SerializeCompressed(), channelCapacity) + witnessScript, multiSigOut, err := GenFundingPkScript( + ourKey.PubKey.SerializeCompressed(), + theirKey.PubKey.SerializeCompressed(), channelCapacity, + ) if err != nil { req.err <- err return @@ -850,22 +823,22 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) { var stateObfuscator [StateHintSize]byte if chanState.ChanType == channeldb.SingleFunder { stateObfuscator = DeriveStateHintObfuscator( - ourContribution.PaymentBasePoint, - theirContribution.PaymentBasePoint, + ourContribution.PaymentBasePoint.PubKey, + theirContribution.PaymentBasePoint.PubKey, ) } else { - ourSer := ourContribution.PaymentBasePoint.SerializeCompressed() - theirSer := theirContribution.PaymentBasePoint.SerializeCompressed() + ourSer := ourContribution.PaymentBasePoint.PubKey.SerializeCompressed() + theirSer := theirContribution.PaymentBasePoint.PubKey.SerializeCompressed() switch bytes.Compare(ourSer, theirSer) { case -1: stateObfuscator = DeriveStateHintObfuscator( - ourContribution.PaymentBasePoint, - theirContribution.PaymentBasePoint, + ourContribution.PaymentBasePoint.PubKey, + theirContribution.PaymentBasePoint.PubKey, ) default: stateObfuscator = DeriveStateHintObfuscator( - theirContribution.PaymentBasePoint, - ourContribution.PaymentBasePoint, + theirContribution.PaymentBasePoint.PubKey, + ourContribution.PaymentBasePoint.PubKey, ) } } @@ -895,7 +868,7 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) { // transaction. signDesc = SignDescriptor{ WitnessScript: witnessScript, - PubKey: ourKey, + KeyDesc: ourKey, Output: multiSigOut, HashType: txscript.SigHashAll, 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 // witnessScript script, but include the p2sh output as the subscript // for verification. - witnessScript, _, err := GenFundingPkScript(ourKey.SerializeCompressed(), - theirKey.SerializeCompressed(), int64(res.partialState.Capacity)) + witnessScript, _, err := GenFundingPkScript( + ourKey.PubKey.SerializeCompressed(), + theirKey.PubKey.SerializeCompressed(), + int64(res.partialState.Capacity), + ) if err != nil { msg.err <- err msg.completeChan <- nil @@ -1066,7 +1042,7 @@ func (l *LightningWallet) handleFundingCounterPartySigs(msg *addCounterPartySigs msg.err <- err msg.completeChan <- nil 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.completeChan <- nil return @@ -1168,8 +1144,9 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) { // generator state obfuscator to encode the current state number within // both commitment transactions. stateObfuscator := DeriveStateHintObfuscator( - pendingReservation.theirContribution.PaymentBasePoint, - pendingReservation.ourContribution.PaymentBasePoint) + pendingReservation.theirContribution.PaymentBasePoint.PubKey, + pendingReservation.ourContribution.PaymentBasePoint.PubKey, + ) err = initStateHints(ourCommitTx, theirCommitTx, stateObfuscator) if err != nil { req.err <- err @@ -1194,8 +1171,10 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) { hashCache := txscript.NewTxSigHashes(ourCommitTx) theirKey := pendingReservation.theirContribution.MultiSigKey ourKey := pendingReservation.ourContribution.MultiSigKey - witnessScript, _, err := GenFundingPkScript(ourKey.SerializeCompressed(), - theirKey.SerializeCompressed(), channelValue) + witnessScript, _, err := GenFundingPkScript( + ourKey.PubKey.SerializeCompressed(), + theirKey.PubKey.SerializeCompressed(), channelValue, + ) if err != nil { req.err <- err req.completeChan <- nil @@ -1217,8 +1196,9 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) { req.err <- err req.completeChan <- nil return - } else if !sig.Verify(sigHash, theirKey) { - req.err <- fmt.Errorf("counterparty's commitment signature is invalid") + } else if !sig.Verify(sigHash, theirKey.PubKey) { + req.err <- fmt.Errorf("counterparty's commitment signature " + + "is invalid") req.completeChan <- nil return } @@ -1235,7 +1215,7 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) { } signDesc := SignDescriptor{ WitnessScript: witnessScript, - PubKey: ourKey, + KeyDesc: ourKey, Output: &wire.TxOut{ PkScript: p2wsh, Value: channelValue,