order+funding: use correct multisig key

If we're the provider of a sidecar channel then we don't actually have
the private key of the funding multisig key. Therefore we cannot
re-derive it on demand and have to use the key provided in the sidecar
ticket instead.
This commit is contained in:
Oliver Gugger 2021-03-19 16:49:03 +01:00
parent 482f4828b9
commit 71b7f419d8
No known key found for this signature in database
GPG key ID: 8E4256593F177720
4 changed files with 278 additions and 49 deletions

View file

@ -305,12 +305,15 @@ func (m *Manager) deriveFundingShim(ourOrder order.Order,
thawHeight uint32
selfChanBalance btcutil.Amount
)
if ourOrder.Type() == order.TypeBid {
ourOrderBid, ourOrderIsBid := ourOrder.(*order.Bid)
ourOrderIsSidecar := ourOrderIsBid && ourOrderBid.SidecarTicket != nil
if ourOrderIsBid {
bidNonce = ourOrder.Nonce()
askNonce = matchedOrder.Order.Nonce()
thawHeight = ourOrder.(*order.Bid).LeaseDuration
selfChanBalance = ourOrder.(*order.Bid).SelfChanBalance
thawHeight = ourOrderBid.LeaseDuration
selfChanBalance = ourOrderBid.SelfChanBalance
} else {
bidNonce = matchedOrder.Order.Nonce()
askNonce = ourOrder.Nonce()
@ -326,15 +329,30 @@ func (m *Manager) deriveFundingShim(ourOrder order.Order,
// Next, we'll need to find the location of this channel output on the
// funding transaction, so we'll re-compute the funding script from
// scratch.
ctxb := context.Background()
ourKeyLocator := ourOrder.Details().MultiSigKeyLocator
ourMultiSigKey, err := m.cfg.WalletKit.DeriveKey(
ctxb, &ourKeyLocator,
)
if err != nil {
return nil, [32]byte{}, err
// scratch. If we're a taker or otherwise uninvolved in a sidecar order
// we have to re-derive our key from the locator.
var ourMultiSigKey *keychain.KeyDescriptor
if ourOrderIsSidecar && ourOrderBid.SidecarTicket.Recipient != nil {
recipient := ourOrderBid.SidecarTicket.Recipient
ourMultiSigKey = &keychain.KeyDescriptor{
KeyLocator: keychain.KeyLocator{
Family: keychain.KeyFamilyMultiSig,
Index: recipient.MultiSigKeyIndex,
},
PubKey: recipient.MultiSigPubKey,
}
} else {
var err error
ctxb := context.Background()
ourKeyLocator := ourOrder.Details().MultiSigKeyLocator
ourMultiSigKey, err = m.cfg.WalletKit.DeriveKey(
ctxb, &ourKeyLocator,
)
if err != nil {
return nil, [32]byte{}, err
}
}
_, fundingOutput, err := input.GenFundingPkScript(
ourMultiSigKey.PubKey.SerializeCompressed(),
matchedOrder.MultiSigKey[:], int64(chanSize),
@ -367,8 +385,8 @@ func (m *Manager) deriveFundingShim(ourOrder order.Order,
LocalKey: &lnrpc.KeyDescriptor{
RawKeyBytes: ourMultiSigKey.PubKey.SerializeCompressed(),
KeyLoc: &lnrpc.KeyLocator{
KeyFamily: int32(ourKeyLocator.Family),
KeyIndex: int32(ourKeyLocator.Index),
KeyFamily: int32(ourMultiSigKey.Family),
KeyIndex: int32(ourMultiSigKey.Index),
},
},
RemoteKey: matchedOrder.MultiSigKey[:],

View file

@ -21,6 +21,7 @@ import (
"github.com/lightninglabs/pool/clientdb"
"github.com/lightninglabs/pool/internal/test"
"github.com/lightninglabs/pool/order"
"github.com/lightninglabs/pool/sidecar"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnrpc"
@ -384,7 +385,6 @@ func TestFundingManager(t *testing.T) {
},
BatchTX: batchTx,
}
txidHash := batchTx.TxHash()
pendingChanID := order.PendingChanKey(ask.Nonce(), bid.Nonce())
// Make sure the channel preparations work as expected. We expect the
@ -410,28 +410,15 @@ func TestFundingManager(t *testing.T) {
// Validate the shim.
shim := h.baseClientMock.fundingShims[pendingChanID]
require.NotNil(t, shim)
require.Equal(t, uint32(2500), shim.ThawHeight)
require.Equal(t, int64(order.SupplyUnit(4).ToSatoshis()), shim.Amt)
chanPoint := &lnrpc.ChannelPoint{
FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
FundingTxidBytes: txidHash[:],
},
OutputIndex: 1,
}
require.Equal(t, chanPoint, shim.ChanPoint)
require.Equal(
t, pubKeyBid.SerializeCompressed(), shim.LocalKey.RawKeyBytes,
)
require.Equal(
t, int32(bid.Kit.MultiSigKeyLocator.Family),
shim.LocalKey.KeyLoc.KeyFamily,
)
require.Equal(
t, int32(bid.Kit.MultiSigKeyLocator.Index),
shim.LocalKey.KeyLoc.KeyIndex,
)
require.Equal(t, matchedAsk.MultiSigKey[:], shim.RemoteKey)
assertFundingShim(
t, shim, uint32(2500), int64(order.SupplyUnit(4).ToSatoshis()),
batchTx, 1, &keychain.KeyDescriptor{
KeyLocator: keychain.KeyLocator{
Family: bid.Kit.MultiSigKeyLocator.Family,
Index: bid.Kit.MultiSigKeyLocator.Index,
},
PubKey: pubKeyBid,
}, matchedAsk.MultiSigKey[:])
// Next, make sure we get a partial reject error if we enable the "new
// nodes only" flag and already have a channel with the matched node.
@ -566,6 +553,136 @@ func callBatchChannelSetup(t *testing.T, h *managerHarness, batch *order.Batch,
require.Equal(t, 2, len(chanInfo))
}
// TestDeriveFundingShim makes sure the correct keys are used for creating a
// funding shim.
func TestDeriveFundingShim(t *testing.T) {
h := newManagerHarness(t)
defer h.stop()
var (
bidKeyIndex int32 = 101
sidecarKeyIndex int32 = 102
_, pubKeyAsk = test.CreateKey(100)
_, pubKeyBid = test.CreateKey(bidKeyIndex)
_, pubKeySidecar = test.CreateKey(sidecarKeyIndex)
askNonce = order.Nonce{1, 2, 3}
bidNonce = order.Nonce{3, 2, 1}
expectedKeyLocator = keychain.KeyLocator{
Family: 1234,
Index: uint32(bidKeyIndex),
}
expectedKeyLocatorSidecar = keychain.KeyLocator{
Family: keychain.KeyFamilyMultiSig,
Index: uint32(sidecarKeyIndex),
}
batchTx = &wire.MsgTx{
TxOut: []*wire.TxOut{{}},
}
)
askKit := order.NewKit(askNonce)
matchedAsk := &order.MatchedOrder{
Order: &order.Ask{Kit: *askKit},
UnitsFilled: 4,
}
copy(matchedAsk.MultiSigKey[:], pubKeyAsk.SerializeCompressed())
// First test is a normal bid where the funding key should be derived
// from the wallet
bid := &order.Bid{
Kit: newKitFromTemplate(bidNonce, &order.Kit{
MultiSigKeyLocator: expectedKeyLocator,
Units: 4,
LeaseDuration: 12345,
}),
}
_, batchTx.TxOut[0], _ = input.GenFundingPkScript(
pubKeyBid.SerializeCompressed(),
matchedAsk.MultiSigKey[:], int64(4*order.BaseSupplyUnit),
)
shim, pendingChanID, err := h.mgr.deriveFundingShim(
bid, matchedAsk, batchTx,
)
require.NoError(t, err)
require.Equal(t, order.PendingChanKey(askNonce, bidNonce), pendingChanID)
require.IsType(t, shim.Shim, &lnrpc.FundingShim_ChanPointShim{})
expectedKeyDescriptor := &keychain.KeyDescriptor{
PubKey: pubKeyBid,
KeyLocator: expectedKeyLocator,
}
chanPointShim := shim.Shim.(*lnrpc.FundingShim_ChanPointShim)
assertFundingShim(
t, chanPointShim.ChanPointShim, 12345, 400_000, batchTx, 0,
expectedKeyDescriptor, pubKeyAsk.SerializeCompressed(),
)
// And the second test is with a sidecar channel bid.
ticket, err := sidecar.NewTicket(
sidecar.VersionDefault, 400_000, 0, 12345, pubKeyBid,
)
require.NoError(t, err)
ticket.Recipient = &sidecar.Recipient{
MultiSigPubKey: pubKeySidecar,
MultiSigKeyIndex: uint32(sidecarKeyIndex),
}
bid = &order.Bid{
Kit: newKitFromTemplate(bidNonce, &order.Kit{
MultiSigKeyLocator: expectedKeyLocator,
Units: 4,
LeaseDuration: 12345,
}),
SidecarTicket: ticket,
}
_, batchTx.TxOut[0], _ = input.GenFundingPkScript(
pubKeySidecar.SerializeCompressed(),
matchedAsk.MultiSigKey[:], int64(4*order.BaseSupplyUnit),
)
shim, pendingChanID, err = h.mgr.deriveFundingShim(
bid, matchedAsk, batchTx,
)
require.NoError(t, err)
require.Equal(t, order.PendingChanKey(askNonce, bidNonce), pendingChanID)
require.IsType(t, shim.Shim, &lnrpc.FundingShim_ChanPointShim{})
expectedKeyDescriptor = &keychain.KeyDescriptor{
PubKey: pubKeySidecar,
KeyLocator: expectedKeyLocatorSidecar,
}
chanPointShim = shim.Shim.(*lnrpc.FundingShim_ChanPointShim)
assertFundingShim(
t, chanPointShim.ChanPointShim, 12345, 400_000, batchTx, 0,
expectedKeyDescriptor, pubKeyAsk.SerializeCompressed(),
)
}
// assertFundingShim asserts that the funding shim contains the data that we
// expect.
func assertFundingShim(t *testing.T, shim *lnrpc.ChanPointShim,
thawHeight uint32, amt int64, fundingTx *wire.MsgTx,
fundingOutputIndex uint32, localKey *keychain.KeyDescriptor,
remoteKey []byte) {
require.Equal(t, thawHeight, shim.ThawHeight)
require.Equal(t, amt, shim.Amt)
fundingTxHash := fundingTx.TxHash()
chanPoint := &lnrpc.ChannelPoint{
FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
FundingTxidBytes: fundingTxHash[:],
},
OutputIndex: fundingOutputIndex,
}
require.Equal(t, chanPoint, shim.ChanPoint)
require.Equal(
t, localKey.PubKey.SerializeCompressed(),
shim.LocalKey.RawKeyBytes,
)
require.Equal(t, int32(localKey.Family), shim.LocalKey.KeyLoc.KeyFamily)
require.Equal(t, int32(localKey.Index), shim.LocalKey.KeyLoc.KeyIndex)
require.Equal(t, remoteKey, shim.RemoteKey)
}
// TestWaitForPeerConnections tests the function that waits for peer connections
// to be established before continuing with the funding process.
func TestWaitForPeerConnections(t *testing.T) {

View file

@ -213,17 +213,31 @@ type Fetcher func(Nonce) (Order, error)
func ChannelOutput(batchTx *wire.MsgTx, wallet lndclient.WalletKitClient,
ourOrder Order, otherOrder *MatchedOrder) (*wire.TxOut, uint32, error) {
// Re-derive our multisig key first.
ctxt, cancel := context.WithTimeout(
context.Background(), deriveKeyTimeout,
)
defer cancel()
ourKey, err := wallet.DeriveKey(
ctxt, &ourOrder.Details().MultiSigKeyLocator,
)
if err != nil {
return nil, 0, fmt.Errorf("could not derive our multisig key: "+
"%v", err)
var ourKey []byte
ourOrderBid, isBid := ourOrder.(*Bid)
if isBid && ourOrderBid.SidecarTicket != nil {
r := ourOrderBid.SidecarTicket.Recipient
if r == nil || r.MultiSigPubKey == nil {
return nil, 0, fmt.Errorf("recipient information in " +
"sidecar ticket missing")
}
ourKey = r.MultiSigPubKey.SerializeCompressed()
} else {
// Re-derive our multisig key first.
ctxt, cancel := context.WithTimeout(
context.Background(), deriveKeyTimeout,
)
defer cancel()
ourKeyDesc, err := wallet.DeriveKey(
ctxt, &ourOrder.Details().MultiSigKeyLocator,
)
if err != nil {
return nil, 0, fmt.Errorf("could not derive our "+
"multisig key: %v", err)
}
ourKey = ourKeyDesc.PubKey.SerializeCompressed()
}
// A self channel balance is added on top of the number of units filled.
@ -237,8 +251,7 @@ func ChannelOutput(batchTx *wire.MsgTx, wallet lndclient.WalletKitClient,
// Gather the information we expect to find in the batch TX.
expectedOutputSize := selfChanBalance + otherOrder.UnitsFilled.ToSatoshis()
_, expectedOut, err := input.GenFundingPkScript(
ourKey.PubKey.SerializeCompressed(), otherOrder.MultiSigKey[:],
int64(expectedOutputSize),
ourKey, otherOrder.MultiSigKey[:], int64(expectedOutputSize),
)
if err != nil {
return nil, 0, fmt.Errorf("could not create multisig script: "+

View file

@ -5,7 +5,12 @@ import (
"testing"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/wire"
"github.com/lightninglabs/pool/internal/test"
"github.com/lightninglabs/pool/poolscript"
"github.com/lightninglabs/pool/sidecar"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain"
"github.com/stretchr/testify/require"
)
@ -45,3 +50,79 @@ func TestDecrementingBatchIDs(t *testing.T) {
// library prints the whole result which is huuuuuge.
require.Equal(t, MaxBatchIDHistoryLookup, len(ids))
}
// TestChannelOutput makes sure the correct keys are used for looking up an
// expected channel output.
func TestChannelOutput(t *testing.T) {
var (
bidKeyIndex int32 = 101
sidecarKeyIndex int32 = 102
_, pubKeyAsk = test.CreateKey(100)
_, pubKeyBid = test.CreateKey(bidKeyIndex)
_, pubKeySidecar = test.CreateKey(sidecarKeyIndex)
mockWallet = test.NewMockWalletKit()
askNonce = Nonce{1, 2, 3}
bidNonce = Nonce{3, 2, 1}
batchTx = &wire.MsgTx{
TxOut: []*wire.TxOut{{}},
}
)
askKit := NewKit(askNonce)
matchedAsk := &MatchedOrder{
Order: &Ask{Kit: *askKit},
UnitsFilled: 4,
}
copy(matchedAsk.MultiSigKey[:], pubKeyAsk.SerializeCompressed())
// First test is a normal bid where the funding key should be derived
// from the wallet
bid := &Bid{
Kit: newKitFromTemplate(bidNonce, &Kit{
MultiSigKeyLocator: keychain.KeyLocator{
Family: 1234,
Index: uint32(bidKeyIndex),
},
Units: 4,
LeaseDuration: 12345,
}),
}
_, batchTx.TxOut[0], _ = input.GenFundingPkScript(
pubKeyBid.SerializeCompressed(),
matchedAsk.MultiSigKey[:], int64(4*BaseSupplyUnit),
)
out, idx, err := ChannelOutput(batchTx, mockWallet, bid, matchedAsk)
require.NoError(t, err)
require.Equal(t, uint32(0), idx)
require.Equal(t, batchTx.TxOut[0], out)
// And the second test is with a sidecar channel bid.
ticket, err := sidecar.NewTicket(
sidecar.VersionDefault, 400_000, 0, 12345, pubKeyBid,
)
require.NoError(t, err)
ticket.Recipient = &sidecar.Recipient{
MultiSigPubKey: pubKeySidecar,
MultiSigKeyIndex: uint32(sidecarKeyIndex),
}
bid = &Bid{
Kit: newKitFromTemplate(bidNonce, &Kit{
MultiSigKeyLocator: keychain.KeyLocator{
Family: 1234,
Index: uint32(bidKeyIndex),
},
Units: 4,
LeaseDuration: 12345,
}),
SidecarTicket: ticket,
}
_, batchTx.TxOut[0], _ = input.GenFundingPkScript(
pubKeySidecar.SerializeCompressed(),
matchedAsk.MultiSigKey[:], int64(4*BaseSupplyUnit),
)
out, idx, err = ChannelOutput(batchTx, mockWallet, bid, matchedAsk)
require.NoError(t, err)
require.Equal(t, uint32(0), idx)
require.Equal(t, batchTx.TxOut[0], out)
}