mirror of
https://github.com/lightninglabs/pool.git
synced 2026-08-13 12:33:04 +02:00
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:
parent
482f4828b9
commit
71b7f419d8
4 changed files with 278 additions and 49 deletions
|
|
@ -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[:],
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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: "+
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue