mirror of
https://github.com/lightninglabs/pool.git
synced 2026-08-13 12:33:04 +02:00
server+funding: add sidecar offer to funding manager
With this commit we add the business logic for creating a sidecar channel offer as the provider. We choose the funding manager as the place for this logic as that already has most of the required lnd clients and will ultimately be responsible for kickstarting the funding of the sidecar channel on the provider's side.
This commit is contained in:
parent
27ed02a5c4
commit
bc9bf56a8d
3 changed files with 146 additions and 4 deletions
|
|
@ -11,6 +11,7 @@ import (
|
|||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
|
|
@ -19,7 +20,9 @@ import (
|
|||
"github.com/lightninglabs/pool/chaninfo"
|
||||
"github.com/lightninglabs/pool/clientdb"
|
||||
"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"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
"github.com/lightningnetwork/lnd/subscribe"
|
||||
|
|
@ -31,6 +34,13 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
// nodeIdentityKeyLoc is the key locator from which the identity key of
|
||||
// an lnd node is derived.
|
||||
nodeIdentityKeyLoc = keychain.KeyLocator{
|
||||
Family: keychain.KeyFamilyNodeKey,
|
||||
Index: 0,
|
||||
}
|
||||
|
||||
// rpcCodeFundingFailed is the error code we send if the channel funding
|
||||
// fails because of a timeout or another problem.
|
||||
rpcCodeFundingFailed = auctioneerrpc.OrderReject_CHANNEL_FUNDING_FAILED
|
||||
|
|
@ -106,10 +116,16 @@ type ManagerConfig struct {
|
|||
// LightningClient is an lndclient wrapped lnrpc client.
|
||||
LightningClient lndclient.LightningClient
|
||||
|
||||
// SignerClient is an lndclient wrapped signrpc client.
|
||||
SignerClient lndclient.SignerClient
|
||||
|
||||
// BaseClient is a raw lnrpc client that implements all methods the
|
||||
// funding manager needs.
|
||||
BaseClient BaseClient
|
||||
|
||||
// NodePubKey is the connected lnd node's identity public key.
|
||||
NodePubKey *btcec.PublicKey
|
||||
|
||||
// NewNodesOnly specifies if the funding manager should only accept
|
||||
// matched orders with channels from new nodes that the connected lnd
|
||||
// node doesn't already have channels with.
|
||||
|
|
@ -864,6 +880,44 @@ func (m *Manager) RemovePendingBatchArtifacts(
|
|||
return nil
|
||||
}
|
||||
|
||||
// OfferSidecar creates a sidecar channel offer and embeds it in a new sidecar
|
||||
// ticket. The offer is signed with the local lnd's node public key.
|
||||
func (m *Manager) OfferSidecar(ctx context.Context, capacity,
|
||||
pushAmt btcutil.Amount, duration uint32) (*sidecar.Ticket, error) {
|
||||
|
||||
// Make sure the capacity and push amounts are sane.
|
||||
err := sidecar.CheckOfferParams(capacity, pushAmt, order.BaseSupplyUnit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// So far everything looks good. Let's create the ticket with the offer
|
||||
// now.
|
||||
ticket, err := sidecar.NewTicket(
|
||||
sidecar.VersionDefault, capacity, pushAmt, duration,
|
||||
m.cfg.NodePubKey,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating sidecar ticket: %v", err)
|
||||
}
|
||||
|
||||
// Let's sign the offer part of the ticket with our node's identity key
|
||||
// now.
|
||||
if err := sidecar.SignOffer(
|
||||
ctx, ticket, nodeIdentityKeyLoc, m.cfg.SignerClient,
|
||||
); err != nil {
|
||||
return nil, fmt.Errorf("error signing offer: %v", err)
|
||||
}
|
||||
|
||||
// Let's now store and return the ticket with the signed offer.
|
||||
err = m.cfg.DB.AddSidecar(ticket)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error storing sidecar ticket: %v", err)
|
||||
}
|
||||
|
||||
return ticket, nil
|
||||
}
|
||||
|
||||
// connectToMatchedTrader attempts to connect to a trader that we've had an
|
||||
// order matched with, on all available addresses.
|
||||
func (m *Manager) connectToMatchedTrader(ctx context.Context,
|
||||
|
|
|
|||
|
|
@ -2,14 +2,17 @@ package funding
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"net"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightninglabs/lndclient"
|
||||
|
|
@ -240,6 +243,7 @@ type managerHarness struct {
|
|||
quit chan struct{}
|
||||
lnMock *test.MockLightning
|
||||
baseClientMock *fundingBaseClientMock
|
||||
signerMock *test.MockSigner
|
||||
mgr *Manager
|
||||
}
|
||||
|
||||
|
|
@ -256,6 +260,7 @@ func newManagerHarness(t *testing.T) *managerHarness {
|
|||
quit := make(chan struct{})
|
||||
lightningClient := test.NewMockLightning()
|
||||
walletKitClient := test.NewMockWalletKit()
|
||||
signerClient := test.NewMockSigner()
|
||||
baseClientMock := &fundingBaseClientMock{
|
||||
lightningClient: lightningClient,
|
||||
fundingShims: make(map[[32]byte]*lnrpc.ChanPointShim),
|
||||
|
|
@ -266,10 +271,16 @@ func newManagerHarness(t *testing.T) *managerHarness {
|
|||
quit: quit,
|
||||
}
|
||||
mgr := NewManager(&ManagerConfig{
|
||||
DB: db,
|
||||
WalletKit: walletKitClient,
|
||||
LightningClient: lightningClient,
|
||||
BaseClient: baseClientMock,
|
||||
DB: db,
|
||||
WalletKit: walletKitClient,
|
||||
LightningClient: lightningClient,
|
||||
BaseClient: baseClientMock,
|
||||
SignerClient: signerClient,
|
||||
NodePubKey: &btcec.PublicKey{
|
||||
Curve: btcec.S256(),
|
||||
X: new(big.Int),
|
||||
Y: new(big.Int),
|
||||
},
|
||||
NewNodesOnly: true,
|
||||
BatchStepTimeout: 400 * time.Millisecond,
|
||||
})
|
||||
|
|
@ -284,6 +295,7 @@ func newManagerHarness(t *testing.T) *managerHarness {
|
|||
quit: quit,
|
||||
lnMock: lightningClient,
|
||||
baseClientMock: baseClientMock,
|
||||
signerMock: signerClient,
|
||||
mgr: mgr,
|
||||
}
|
||||
}
|
||||
|
|
@ -636,6 +648,80 @@ func TestWaitForPeerConnections(t *testing.T) {
|
|||
require.Equal(t, expectedErr, err)
|
||||
}
|
||||
|
||||
// TestOfferSidecarValidation checks the validation logic in the sidecar
|
||||
// offering process.
|
||||
func TestOfferSidecarValidation(t *testing.T) {
|
||||
h := newManagerHarness(t)
|
||||
defer h.stop()
|
||||
|
||||
negativeCases := []struct {
|
||||
name string
|
||||
capacity btcutil.Amount
|
||||
pushAmt btcutil.Amount
|
||||
expectedErr string
|
||||
}{{
|
||||
name: "empty capacity",
|
||||
expectedErr: "channel capacity must be positive multiple of",
|
||||
}, {
|
||||
name: "invalid capacity",
|
||||
capacity: 123,
|
||||
expectedErr: "channel capacity must be positive multiple of",
|
||||
}, {
|
||||
name: "invalid push amount",
|
||||
capacity: 100000,
|
||||
pushAmt: 100001,
|
||||
expectedErr: "self channel balance must be smaller than " +
|
||||
"or equal to capacity",
|
||||
}}
|
||||
for _, testCase := range negativeCases {
|
||||
_, err := h.mgr.OfferSidecar(
|
||||
context.Background(), testCase.capacity,
|
||||
testCase.pushAmt, 2016,
|
||||
)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), testCase.expectedErr)
|
||||
}
|
||||
}
|
||||
|
||||
// TestOfferSidecar makes sure sidecar offers can be created and signed with the
|
||||
// lnd node's identity key.
|
||||
func TestOfferSidecar(t *testing.T) {
|
||||
h := newManagerHarness(t)
|
||||
defer h.stop()
|
||||
|
||||
// We'll need a formally valid signature to pass the parsing. So we'll
|
||||
// just create a dummy signature from a random key pair.
|
||||
privKey, err := btcec.NewPrivateKey(btcec.S256())
|
||||
require.NoError(t, err)
|
||||
hash := sha256.New()
|
||||
_, _ = hash.Write([]byte("foo"))
|
||||
digest := hash.Sum(nil)
|
||||
sig, err := privKey.Sign(digest)
|
||||
require.NoError(t, err)
|
||||
|
||||
h.mgr.cfg.NodePubKey = privKey.PubKey()
|
||||
h.signerMock.Signature = sig.Serialize()
|
||||
var nodeKeyRaw [33]byte
|
||||
copy(nodeKeyRaw[:], privKey.PubKey().SerializeCompressed())
|
||||
|
||||
// Let's create our offer now.
|
||||
capacity, pushAmt := btcutil.Amount(100_000), btcutil.Amount(40_000)
|
||||
ticket, err := h.mgr.OfferSidecar(
|
||||
context.Background(), capacity, pushAmt, 2016,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, capacity, ticket.Offer.Capacity)
|
||||
require.Equal(t, pushAmt, ticket.Offer.PushAmt)
|
||||
require.Equal(t, privKey.PubKey(), ticket.Offer.SignPubKey)
|
||||
require.Equal(t, sig, ticket.Offer.SigOfferDigest)
|
||||
|
||||
// Make sure the DB has the exact same ticket now.
|
||||
dbTicket, err := h.db.Sidecar(ticket.ID, privKey.PubKey())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, ticket, dbTicket)
|
||||
}
|
||||
|
||||
func newKitFromTemplate(nonce order.Nonce, tpl *order.Kit) order.Kit {
|
||||
kit := order.NewKit(nonce)
|
||||
kit.Version = tpl.Version
|
||||
|
|
|
|||
|
|
@ -474,7 +474,9 @@ func (s *Server) setupClient() error {
|
|||
DB: s.db,
|
||||
WalletKit: s.lndServices.WalletKit,
|
||||
LightningClient: s.lndServices.Client,
|
||||
SignerClient: s.lndServices.Signer,
|
||||
BaseClient: s.lndClient,
|
||||
NodePubKey: nodePubKey,
|
||||
BatchStepTimeout: order.DefaultBatchStepTimeout,
|
||||
NewNodesOnly: s.cfg.NewNodesOnly,
|
||||
NotifyShimCreated: channelAcceptor.ShimRegistered,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue