func (c *Client) ReserveAccount(ctx context.Context, value btcutil.Amount,
	expiry uint32, traderKey *btcec.PublicKey) (*account.Reservation,
	error) {
	resp, err := c.client.ReserveAccount(
		ctx, &auctioneerrpc.ReserveAccountRequest{
			AccountValue:  uint64(value),
			TraderKey:     traderKey.SerializeCompressed(),
			AccountExpiry: expiry,
		},
	)
	if err != nil {
		return nil, err
	}
	auctioneerKey, err := btcec.ParsePubKey(
		resp.AuctioneerKey, btcec.S256(),
	)
	if err != nil {
		return nil, err
	}
	initialBatchKey, err := btcec.ParsePubKey(
		resp.InitialBatchKey, btcec.S256(),
	)
	if err != nil {
		return nil, err
	}
	return &account.Reservation{
		AuctioneerKey:   auctioneerKey,
		InitialBatchKey: initialBatchKey,
	}, nil
}
ReserveAccount reserves an account with the auctioneer. It returns the base
public key we should use for them in our 2-of-2 multi-sig construction, and
the initial batch key.
package auctioneer
import (
	"bytes"
	"context"
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"math"
	"net"
	"sync"
	"sync/atomic"
	"time"
	"github.com/btcsuite/btcd/btcec"
	"github.com/btcsuite/btcd/chaincfg/chainhash"
	"github.com/btcsuite/btcd/wire"
	"github.com/btcsuite/btcutil"
	"github.com/lightninglabs/lndclient"
	"github.com/lightninglabs/pool/account"
	"github.com/lightninglabs/pool/auctioneerrpc"
	"github.com/lightninglabs/pool/order"
	"github.com/lightninglabs/pool/poolrpc"
	"github.com/lightninglabs/pool/sidecar"
	"github.com/lightninglabs/pool/terms"
	"github.com/lightningnetwork/lnd/keychain"
	"github.com/lightningnetwork/lnd/lntypes"
	"github.com/lightningnetwork/lnd/lnwallet/chainfee"
	"github.com/lightningnetwork/lnd/tor"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/status"
)
const (
	reconnectRetries = math.MaxInt16
	maxUnusedAccountKeyLookup = 50
)
var (
	ErrServerShutdown = errors.New("server shutting down")
	ErrServerErrored = errors.New("server sent unexpected error")
	ErrClientShutdown = errors.New("client shutting down")
	ErrAuthCanceled = errors.New("authentication was canceled")
)
type Config struct {
	ServerAddress string
	ProxyAddress string
	Insecure bool
	TLSPathServer string
	DialOpts []grpc.DialOption
	Signer lndclient.SignerClient
	MinBackoff time.Duration
	MaxBackoff time.Duration
	BatchSource BatchSource
	BatchCleaner BatchCleaner
	GenUserAgent func(context.Context) string
	ConnectSidecar bool
}
type Client struct {
	cfg *Config
	started uint32
	stopped uint32
	StreamErrChan  chan error
	errChanSwitch  *ErrChanSwitch
	FromServerChan chan *auctioneerrpc.ServerAuctionMessage
	serverConn     *grpc.ClientConn
	client         auctioneerrpc.ChannelAuctioneerClient
	hashMailClient auctioneerrpc.HashMailClient
	quit         chan struct{}
	wg           sync.WaitGroup
	serverStream auctioneerrpc.ChannelAuctioneer_SubscribeBatchAuctionClient
	streamMutex  sync.Mutex
	streamCancel func()
	subscribedAccts    map[[33]byte]*acctSubscription
	subscribedAcctsMtx sync.Mutex
}
func NewClient(cfg *Config) (*Client, error) {
	var err error
	cfg.DialOpts, err = getAuctionServerDialOpts(
		cfg.Insecure, cfg.ProxyAddress, cfg.TLSPathServer, cfg.DialOpts...,
	)
	if err != nil {
		return nil, err
	}
	mainErrChan := make(chan error)
	errChanSwitch := NewErrChanSwitch(mainErrChan)
	return &Client{
		cfg:             cfg,
		FromServerChan:  make(chan *auctioneerrpc.ServerAuctionMessage),
		StreamErrChan:   mainErrChan,
		errChanSwitch:   errChanSwitch,
		quit:            make(chan struct{}),
		subscribedAccts: make(map[[33]byte]*acctSubscription),
	}, nil
}
func (c *Client) Start() error {
	if !atomic.CompareAndSwapUint32(&c.started, 0, 1) {
		return nil
	}
	serverConn, err := grpc.Dial(c.cfg.ServerAddress, c.cfg.DialOpts...)
	if err != nil {
		return fmt.Errorf("unable to connect to RPC server: %v",
			err)
	}
	c.serverConn = serverConn
	c.client = auctioneerrpc.NewChannelAuctioneerClient(serverConn)
	c.hashMailClient = auctioneerrpc.NewHashMailClient(serverConn)
	c.errChanSwitch.Start()
	return nil
}
func getAuctionServerDialOpts(insecure bool, proxyAddress, tlsPath string,
	dialOpts ...grpc.DialOption) ([]grpc.DialOption, error) {
	opts := dialOpts
	switch {
	case insecure:
		opts = append(opts, grpc.WithInsecure())
	case tlsPath != "":
		creds, err := credentials.NewClientTLSFromFile(tlsPath, "")
		if err != nil {
			return nil, err
		}
		opts = append(opts, grpc.WithTransportCredentials(creds))
	default:
		creds := credentials.NewTLS(&tls.Config{})
		opts = append(opts, grpc.WithTransportCredentials(creds))
	}
	if proxyAddress != "" {
		log.Infof("Proxying connection to auction server "+
			"over Tor SOCKS proxy %v",
			proxyAddress)
		torDialer := func(_ context.Context, addr string) (net.Conn, error) {
			return tor.Dial(
				addr, proxyAddress, false, false,
				tor.DefaultConnTimeout,
			)
		}
		opts = append(opts, grpc.WithContextDialer(torDialer))
	}
	return opts, nil
}
func (c *Client) Stop() error {
	if !atomic.CompareAndSwapUint32(&c.stopped, 0, 1) {
		return nil
	}
	log.Infof("Shutting down auctioneer client")
	close(c.quit)
	err := c.closeStream()
	if err != nil {
		log.Errorf("Unable to close stream: %v", err)
	}
	c.wg.Wait()
	close(c.FromServerChan)
	c.errChanSwitch.Stop()
	return c.serverConn.Close()
}
func (c *Client) closeStream() error {
	c.streamMutex.Lock()
	defer c.streamMutex.Unlock()
	if c.serverStream == nil {
		return nil
	}
	log.Debugf("Closing server stream")
	err := c.serverStream.CloseSend()
	c.streamCancel()
	c.serverStream = nil
	c.subscribedAcctsMtx.Lock()
	for _, subscription := range c.subscribedAccts {
		close(subscription.quit)
		close(subscription.msgChan)
	}
	c.subscribedAcctsMtx.Unlock()
	return err
}