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:
1 file changed
+53
-73
+53
-73
@@ -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,
|
||||||
|
|||||||
Reference in new issue
Block a user