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 2018-02-17 15:20:41 -08:00
parent fe12c908f8
commit a41f00e2d6
No known key found for this signature in database
GPG Key ID: 964EA263DD637C21
1 changed files with 53 additions and 73 deletions

View File

@ -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,