func (a *SidecarAcceptor) InitSidecarMailbox(streamID [64]byte,
	tkt *sidecar.Ticket) error {
	err := a.client.InitTicketCipherBox(context.Background(), streamID, tkt)
	if err != nil && !isErrAlreadyExists(err) {
		return fmt.Errorf("unable to init cipher box: %v", err)
	}
	return nil
}
InitSidecarMailbox attempts to create the mailbox with the given stream ID
using the sidecar ticket authentication mechanism. If the mailbox already
exists, then a nil error is to be returned.
package pool
import (
	"bytes"
	"context"
	"fmt"
	"sync"
	"github.com/btcsuite/btcd/btcec"
	"github.com/davecgh/go-spew/spew"
	"github.com/lightninglabs/lndclient"
	"github.com/lightninglabs/pool/account"
	"github.com/lightninglabs/pool/auctioneer"
	"github.com/lightninglabs/pool/auctioneerrpc"
	"github.com/lightninglabs/pool/clientdb"
	"github.com/lightninglabs/pool/funding"
	"github.com/lightninglabs/pool/order"
	"github.com/lightninglabs/pool/sidecar"
	"github.com/lightningnetwork/lnd/keychain"
	"github.com/lightningnetwork/lnd/subscribe"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)
type SidecarAcceptor struct {
	cfg *SidecarAcceptorConfig
	client                *auctioneer.Client
	pendingOpenChanClient *subscribe.Client
	pendingSidecarOrders    map[order.Nonce]*sidecar.Ticket
	pendingSidecarOrdersMtx sync.Mutex
	pendingBatch            *order.Batch
	sync.Mutex
	negotiators map[[64]byte]*SidecarNegotiator
	quit chan struct{}
	wg   sync.WaitGroup
}
type SidecarAcceptorConfig struct {
	SidecarDB sidecar.Store
	AcctDB account.Store
	Signer lndclient.SignerClient
	Wallet lndclient.WalletKitClient
	BaseClient funding.BaseClient
	Acceptor *ChannelAcceptor
	NodePubKey *btcec.PublicKey
	ClientCfg auctioneer.Config
	PrepareOrder orderPreparer
	FundingManager *funding.Manager
	FetchSidecarBid func(*sidecar.Ticket) (*order.Bid, error)
}
func NewSidecarAcceptor(cfg *SidecarAcceptorConfig) *SidecarAcceptor {
	cfg.ClientCfg.ConnectSidecar = true
	return &SidecarAcceptor{
		cfg:                  cfg,
		pendingSidecarOrders: make(map[order.Nonce]*sidecar.Ticket),
		quit:                 make(chan struct{}),
		negotiators:          make(map[[64]byte]*SidecarNegotiator),
	}
}
func (a *SidecarAcceptor) Start(errChan chan error) error {
	var err error
	a.client, err = auctioneer.NewClient(&a.cfg.ClientCfg)
	if err != nil {
		return fmt.Errorf("error creating auctioneer client: %v", err)
	}
	if err := a.client.Start(); err != nil {
		return fmt.Errorf("error starting auctioneer client: %v", err)
	}
	if err := a.cfg.Acceptor.Start(errChan); err != nil {
		return fmt.Errorf("error starting channel acceptor: %v", err)
	}
	a.pendingOpenChanClient, err = a.cfg.FundingManager.SubscribePendingOpenChan()
	if err != nil {
		return fmt.Errorf("error subscribing to pending open channel "+
			"events: %v", err)
	}
	tickets, err := a.cfg.SidecarDB.Sidecars()
	if err != nil {
		return fmt.Errorf("error reading sidecar tickets: %v", err)
	}
	for _, ticket := range tickets {
		switch {
		case ticket.Offer.Auto && !ticket.State.IsTerminal():
			acct, err := a.cfg.AcctDB.Account(ticket.Offer.SignPubKey)
			switch {
			case err == clientdb.ErrAccountNotFound:
				autoAcceptor := NewSidecarNegotiator(AutoAcceptorConfig{
					Provider: false,
					StartingPkt: &SidecarPacket{
						CurrentState:   ticket.State,
						ReceiverTicket: ticket,
						ProviderTicket: ticket,
					},
					Driver:  a,
					MailBox: a,
				})
				if err := autoAcceptor.Start(); err != nil {
					return err
				}
				streamID, err := deriveRecipientStreamID(ticket)
				if err != nil {
					return fmt.Errorf("unable to derive "+
						"stream IDs: %v", err)
				}
				a.Lock()
				a.negotiators[streamID] = autoAcceptor
				a.Unlock()
			case err == nil:
				ticketBid, err := a.cfg.FetchSidecarBid(ticket)
				if err != nil {
					return fmt.Errorf("unable to fetch "+
						"sidecar bid: %w", err)
				}
				state := ticket.State
				if state == sidecar.StateOffered {
					state = sidecar.StateCreated
				}
				autoAcceptor := NewSidecarNegotiator(AutoAcceptorConfig{
					Provider:    true,
					ProviderBid: ticketBid,
					StartingPkt: &SidecarPacket{
						CurrentState:   state,
						ReceiverTicket: ticket,
						ProviderTicket: ticket,
					},
					ProviderAccount: acct,
					Driver:          a,
					MailBox:         a,
				})
				if err := autoAcceptor.Start(); err != nil {
					return err
				}
				streamID, err := deriveRecipientStreamID(ticket)
				if err != nil {
					return fmt.Errorf("unable to derive "+
						"stream IDs: %v", err)
				}
				a.Lock()
				a.negotiators[streamID] = autoAcceptor
				a.Unlock()
			default:
				return fmt.Errorf("unable to fetch account "+
					"for sidecar: %w", err)
			}
			continue
		case ticket.State != sidecar.StateExpectingChannel:
			continue
		case ticket.Recipient == nil:
			continue
		}
		r := ticket.Recipient
		if !r.NodePubKey.IsEqual(a.cfg.NodePubKey) {
			continue
		}
		ctxb := context.Background()
		if err := a.ExpectChannel(ctxb, ticket); err != nil {
			return fmt.Errorf("error subscribing to batch "+
				"updates for sidecar ticket: %v", err)
		}
	}
	a.wg.Add(1)
	go a.subscribe()
	return nil
}
func (a *SidecarAcceptor) subscribe() {
	defer a.wg.Done()
	for {
		select {
		case serverMsg, ok := <-a.client.FromServerChan:
			if !ok {
				return
			}
			if err := a.handleServerMessage(serverMsg); err != nil {
				sdcrLog.Errorf("Error while handling server "+
					"message: %v", err)
			}
		case <-a.quit:
			return
		}
	}
}
func (a *SidecarAcceptor) Stop() error {
	var returnErr error
	if err := a.client.Stop(); err != nil {
		sdcrLog.Errorf("Error stopping auctioneer client: %v", err)
		returnErr = err
	}
	for _, negotiator := range a.negotiators {
		negotiator.Stop()
	}
	a.pendingOpenChanClient.Cancel()
	a.cfg.Acceptor.Stop()
	close(a.quit)
	a.wg.Wait()
	return returnErr
}
func (a *SidecarAcceptor) RegisterSidecar(ctx context.Context,
	ticket sidecar.Ticket) (*sidecar.Ticket, error) {
	if err := sidecar.VerifyOffer(ctx, &ticket, a.cfg.Signer); err != nil {
		return nil, fmt.Errorf("error verifying sidecar offer: %v", err)
	}
	_, err := a.cfg.SidecarDB.Sidecar(ticket.ID, ticket.Offer.SignPubKey)
	if err != clientdb.ErrNoSidecar {
		return nil, fmt.Errorf("ticket with ID %x already exists",
			ticket.ID[:])
	}
	keyDesc, err := a.cfg.Wallet.DeriveNextKey(
		ctx, int32(keychain.KeyFamilyMultiSig),
	)
	if err != nil {
		return nil, fmt.Errorf("error deriving multisig key: %v", err)
	}
	ticket.State = sidecar.StateRegistered
	ticket.Recipient = &sidecar.Recipient{
		NodePubKey:       a.cfg.NodePubKey,
		MultiSigPubKey:   keyDesc.PubKey,
		MultiSigKeyIndex: keyDesc.Index,
	}
	if err := a.cfg.SidecarDB.AddSidecar(&ticket); err != nil {
		return nil, fmt.Errorf("error storing sidecar: %v", err)
	}
	return &ticket, nil
}
func (a *SidecarAcceptor) ExpectChannel(ctx context.Context,
	t *sidecar.Ticket) error {
	if t.Order == nil {
		return fmt.Errorf("order in sidecar ticket is missing")
	}
	a.pendingSidecarOrdersMtx.Lock()
	defer a.pendingSidecarOrdersMtx.Unlock()
	nonce := t.Order.BidNonce
	_, ok := a.pendingSidecarOrders[nonce]
	if ok {
		return fmt.Errorf("sidecar with order nonce %x is already "+
			"registered", nonce[:])
	}
	t.State = sidecar.StateExpectingChannel
	if err := a.cfg.SidecarDB.UpdateSidecar(t); err != nil {
		return fmt.Errorf("error updating sidecar: %v", err)
	}
	a.pendingSidecarOrders[nonce] = t
	return a.client.StartAccountSubscription(ctx, &keychain.KeyDescriptor{
		KeyLocator: keychain.KeyLocator{
			Family: keychain.KeyFamilyMultiSig,
			Index:  t.Recipient.MultiSigKeyIndex,
		},
		PubKey: t.Recipient.MultiSigPubKey,
	})
}
func validateOrderedTicket(ctx context.Context, t *sidecar.Ticket,
	signer lndclient.SignerClient, db sidecar.Store) error {
	if t.State != sidecar.StateOrdered {
		return fmt.Errorf("sidecar ticket in state %v, expected %v",
			t.State, sidecar.StateOrdered)
	}
	if err := sidecar.VerifyOffer(ctx, t, signer); err != nil {
		return fmt.Errorf("error validating order in sidecar "+
			"ticket: %v", err)
	}
	if err := sidecar.VerifyOrder(ctx, t, signer); err != nil {
		return fmt.Errorf("error validating order in sidecar "+
			"ticket: %v", err)
	}
	if _, err := db.Sidecar(t.ID, t.Offer.SignPubKey); err != nil {
		return fmt.Errorf("error looking up sidecar order for "+
			"ticket with ID %x: %v", t.ID[:], err)
	}
	return nil
}
func (a *SidecarAcceptor) AutoAcceptSidecar(ticket *sidecar.Ticket) error {
	log.Infof("Attempting negotiation to receive sidecar ticket: %x",
		ticket.ID[:])
	autoAcceptor := NewSidecarNegotiator(AutoAcceptorConfig{
		Provider: false,
		StartingPkt: &SidecarPacket{
			CurrentState:   sidecar.StateRegistered,
			ReceiverTicket: ticket,
			ProviderTicket: ticket,
		},
		Driver:  a,
		MailBox: a,
	})
	streamID, err := deriveRecipientStreamID(ticket)
	if err != nil {
		return fmt.Errorf("unable to derive "+
			"stream IDs: %v", err)
	}
	a.Lock()
	a.negotiators[streamID] = autoAcceptor
	a.Unlock()
	return autoAcceptor.Start()
}
func (a *SidecarAcceptor) SubmitSidecarOrder(ticket *sidecar.Ticket, bid *order.Bid,
	acct *account.Account) (*sidecar.Ticket, error) {
	bid.SidecarTicket = ticket
	ctx := context.Background()
	auctionTerms, err := a.client.Terms(ctx)
	if err != nil {
		return nil, fmt.Errorf("could not query auctioneer terms: %v", err)
	}
	err = prepareAndSubmitOrder(
		ctx, bid, auctionTerms, acct, a.client, a.cfg.PrepareOrder,
	)
	if err != nil {
		return nil, err
	}
	return bid.SidecarTicket, nil
}
func (a *SidecarAcceptor) CoordinateSidecar(ticket *sidecar.Ticket,
	bid *order.Bid, acct *account.Account) error {
	log.Infof("Attempting negotiation to offer sidecar ticket: %x",
		ticket.ID[:])
	autoAcceptor := NewSidecarNegotiator(AutoAcceptorConfig{
		Provider:    true,
		ProviderBid: bid,
		StartingPkt: &SidecarPacket{
			CurrentState:   sidecar.StateOffered,
			ProviderTicket: ticket,
			ReceiverTicket: ticket,
		},
		ProviderAccount: acct,
		Driver:          a,
		MailBox:         a,
	})
	streamID, err := deriveRecipientStreamID(ticket)
	if err != nil {
		return fmt.Errorf("unable to derive "+
			"stream IDs: %v", err)
	}
	a.Lock()
	a.negotiators[streamID] = autoAcceptor
	a.Unlock()
	return autoAcceptor.Start()
}
func (a *SidecarAcceptor) handleServerMessage(
	serverMsg *auctioneerrpc.ServerAuctionMessage) error {
	a.Lock()
	defer a.Unlock()
	switch msg := serverMsg.Msg.(type) {
	case *auctioneerrpc.ServerAuctionMessage_Prepare:
		batchID := msg.Prepare.BatchId
		sdcrLog.Tracef("Received prepare msg from server, "+
			"batch_id=%x: %v", batchID, spew.Sdump(msg))
		newBatch, err := a.matchPrepare(a.pendingBatch, msg.Prepare)
		if err != nil {
			sdcrLog.Errorf("Error handling prepare message: %v",
				err)
			return a.sendRejectBatch(batchID, err)
		}
		a.pendingBatch = newBatch
	case *auctioneerrpc.ServerAuctionMessage_Sign:
		batchID := msg.Sign.BatchId
		sdcrLog.Tracef("Received sign msg from server, batch_id=%x: %v",
			batchID, spew.Sdump(msg))
		if a.pendingBatch == nil ||
			!bytes.Equal(batchID, a.pendingBatch.ID[:]) {
			err := fmt.Errorf("error processing batch sign "+
				"message, unknown batch with ID %x", batchID)
			sdcrLog.Errorf("Error handling sign message: %v", err)
			return a.sendRejectBatch(batchID, err)
		}
		if err := a.matchSign(a.pendingBatch); err != nil {
			sdcrLog.Errorf("Error handling sign message: %v", err)
			return a.sendRejectBatch(batchID, err)
		}
	case *auctioneerrpc.ServerAuctionMessage_Finalize:
		batchID := msg.Finalize.BatchId
		sdcrLog.Tracef("Received finalize msg from server, "+
			"batch_id=%x: %v", batchID, spew.Sdump(msg))
		batch := a.pendingBatch
		a.pendingBatch = nil
		a.matchFinalize(batch)
	default:
		sdcrLog.Debugf("Received msg %v from auctioneer on sidecar "+
			"client: %v", msg)
	}
	return nil
}
func (a *SidecarAcceptor) matchPrepare(pendingBatch *order.Batch,
	msg *auctioneerrpc.OrderMatchPrepare) (*order.Batch, error) {
	batch, err := order.ParseRPCBatch(msg)
	if err != nil {
		return nil, fmt.Errorf("unable to parse batch: %v", err)
	}
	sdcrLog.Infof("Received PrepareMsg for batch=%x, num_orders=%v",
		batch.ID[:], len(batch.MatchedOrders))
	if pendingBatch != nil {
		if err := a.removeShims(pendingBatch); err != nil {
			return nil, fmt.Errorf("unable to cleanup previous "+
				"batch: %v", err)
		}
	}
	err = a.cfg.FundingManager.PrepChannelFunding(batch, a.getSidecarAsOrder)
	if err != nil {
		return nil, fmt.Errorf("error preparing channel funding: %v",
			err)
	}
	sdcrLog.Infof("Accepting batch=%x", batch.ID[:])
	err = a.client.SendAuctionMessage(&auctioneerrpc.ClientAuctionMessage{
		Msg: &auctioneerrpc.ClientAuctionMessage_Accept{
			Accept: &auctioneerrpc.OrderMatchAccept{
				BatchId: batch.ID[:],
			},
		},
	})
	if err != nil {
		return nil, fmt.Errorf("error sending accept msg: %v", err)
	}
	return batch, nil
}
func (a *SidecarAcceptor) matchSign(batch *order.Batch) error {
	channelInfos, err := a.cfg.FundingManager.SidecarBatchChannelSetup(
		a.pendingBatch, a.pendingOpenChanClient, a.getSidecarAsOrder,
	)
	if err != nil {
		return fmt.Errorf("error setting up channels: %v", err)
	}
	rpcChannelInfos, err := marshallChannelInfo(channelInfos)
	if err != nil {
		return fmt.Errorf("error setting up channels: %v", err)
	}
	sdcrLog.Infof("Received OrderMatchSignBegin for batch=%x, "+
		"num_orders=%v", batch.ID[:], len(batch.MatchedOrders))
	sdcrLog.Infof("Sending OrderMatchSign for batch %x", batch.ID[:])
	return a.client.SendAuctionMessage(&auctioneerrpc.ClientAuctionMessage{
		Msg: &auctioneerrpc.ClientAuctionMessage_Sign{
			Sign: &auctioneerrpc.OrderMatchSign{
				BatchId:      batch.ID[:],
				ChannelInfos: rpcChannelInfos,
			},
		},
	})
}
func (a *SidecarAcceptor) finalizeTicketIfExists(ticket *sidecar.Ticket) {
	streamID, err := deriveRecipientStreamID(ticket)
	if err != nil {
		log.Errorf("unable to derive stream IDs: %v", err)
	}
	negotiator, ok := a.negotiators[streamID]
	if !ok {
		return
	}
	negotiator.TicketExecuted()
	delete(a.negotiators, streamID)
}
func (a *SidecarAcceptor) matchFinalize(batch *order.Batch) {
	sdcrLog.Infof("Received FinalizeMsg for batch=%x", batch.ID[:])
	for ourOrder := range batch.MatchedOrders {
		dummyBid, err := a.getSidecarAsOrder(ourOrder)
		if err != nil {
			continue
		}
		a.pendingSidecarOrdersMtx.Lock()
		ticket := a.pendingSidecarOrders[dummyBid.Nonce()]
		ticket.State = sidecar.StateCompleted
		if err := a.cfg.SidecarDB.UpdateSidecar(ticket); err != nil {
			sdcrLog.Errorf("Error updating sidecar ticket to "+
				"state complete: %v", err)
		}
		delete(a.pendingSidecarOrders, ourOrder)
		a.pendingSidecarOrdersMtx.Unlock()
		a.cfg.Acceptor.ShimRemoved(dummyBid.(*order.Bid))
		a.finalizeTicketIfExists(ticket)
	}
}
func (a *SidecarAcceptor) FinalizeTicket(t *sidecar.Ticket) {
	a.Lock()
	defer a.Unlock()
	a.finalizeTicketIfExists(t)
}
func (a *SidecarAcceptor) getSidecarAsOrder(o order.Nonce) (order.Order, error) {
	a.pendingSidecarOrdersMtx.Lock()
	defer a.pendingSidecarOrdersMtx.Unlock()
	for _, ticket := range a.pendingSidecarOrders {
		if ticket.Order.BidNonce == o {
			kit := order.NewKit(ticket.Order.BidNonce)
			kit.LeaseDuration = ticket.Offer.LeaseDurationBlocks
			return &order.Bid{
				Kit:             *kit,
				SidecarTicket:   ticket,
				SelfChanBalance: ticket.Offer.PushAmt,
			}, nil
		}
	}
	return nil, clientdb.ErrNoOrder
}
func (a *SidecarAcceptor) sendRejectBatch(batchID []byte, failure error) error {
	msg := &auctioneerrpc.ClientAuctionMessage_Reject{
		Reject: &auctioneerrpc.OrderMatchReject{
			BatchId:    batchID,
			Reason:     failure.Error(),
			ReasonCode: auctioneerrpc.OrderMatchReject_BATCH_VERSION_MISMATCH,
		},
	}
	return a.client.SendAuctionMessage(&auctioneerrpc.ClientAuctionMessage{
		Msg: msg,
	})
}
func (a *SidecarAcceptor) removeShims(batch *order.Batch) error {
	if err := funding.CancelPendingFundingShims(
		batch.MatchedOrders, a.cfg.BaseClient, a.getSidecarAsOrder,
	); err != nil {
		return err
	}
	for ourOrder := range batch.MatchedOrders {
		dummyBid, err := a.getSidecarAsOrder(ourOrder)
		if err != nil {
			continue
		}
		a.cfg.Acceptor.ShimRemoved(dummyBid.(*order.Bid))
	}
	return nil
}
func (a *SidecarAcceptor) UpdateSidecar(tkt *sidecar.Ticket) error {
	return a.cfg.SidecarDB.UpdateSidecar(tkt)
}
func (a *SidecarAcceptor) ValidateOrderedTicket(tkt *sidecar.Ticket) error {
	ctx := context.Background()
	return validateOrderedTicket(ctx, tkt, a.cfg.Signer, a.cfg.SidecarDB)
}
func (a *SidecarAcceptor) InitAcctMailbox(streamID [64]byte,
	traderKey *keychain.KeyDescriptor) error {
	err := a.client.InitAccountCipherBox(
		context.Background(), streamID, traderKey,
	)
	if err != nil && !isErrAlreadyExists(err) {
		return fmt.Errorf("unable to init cipher box: %v", err)
	}
	return nil
}