github.com/decred/dcrlnd@v0.7.6/funding/batch.go (about)

     1  package funding
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"crypto/rand"
     7  	"encoding/base64"
     8  	"errors"
     9  	"fmt"
    10  
    11  	"github.com/decred/dcrd/chaincfg/chainhash"
    12  	"github.com/decred/dcrd/chaincfg/v3"
    13  	"github.com/decred/dcrd/dcrutil/v4"
    14  	"github.com/decred/dcrd/wire"
    15  	"github.com/decred/dcrlnd/internal/psbt"
    16  	"github.com/decred/dcrlnd/labels"
    17  	"github.com/decred/dcrlnd/lnrpc"
    18  	"github.com/decred/dcrlnd/lnrpc/walletrpc"
    19  	"github.com/decred/dcrlnd/lnwallet/chanfunding"
    20  	"golang.org/x/sync/errgroup"
    21  )
    22  
    23  var (
    24  	// errShuttingDown is the error that is returned if a signal on the
    25  	// quit channel is received which means the whole server is shutting
    26  	// down.
    27  	errShuttingDown = errors.New("shutting down")
    28  
    29  	// emptyChannelID is a channel ID that consists of all zeros.
    30  	emptyChannelID = [32]byte{}
    31  )
    32  
    33  // batchChannel is a struct that keeps track of a single channel's state within
    34  // the batch funding process.
    35  type batchChannel struct {
    36  	fundingReq    *InitFundingMsg
    37  	pendingChanID [32]byte
    38  	updateChan    chan *lnrpc.OpenStatusUpdate
    39  	errChan       chan error
    40  	fundingAddr   string
    41  	chanPoint     *wire.OutPoint
    42  	isPending     bool
    43  }
    44  
    45  // processPsbtUpdate processes the first channel update message that is sent
    46  // once the initial part of the negotiation has completed and the funding output
    47  // (and therefore address) is known.
    48  func (c *batchChannel) processPsbtUpdate(u *lnrpc.OpenStatusUpdate) error {
    49  	psbtUpdate := u.GetPsbtFund()
    50  	if psbtUpdate == nil {
    51  		return fmt.Errorf("got unexpected channel update %v", u.Update)
    52  	}
    53  
    54  	if psbtUpdate.FundingAmount != int64(c.fundingReq.LocalFundingAmt) {
    55  		return fmt.Errorf("got unexpected funding amount %d, wanted "+
    56  			"%d", psbtUpdate.FundingAmount,
    57  			c.fundingReq.LocalFundingAmt)
    58  	}
    59  
    60  	c.fundingAddr = psbtUpdate.FundingAddress
    61  
    62  	return nil
    63  }
    64  
    65  // processPendingUpdate is the second channel update message that is sent once
    66  // the negotiation with the peer has completed and the channel is now pending.
    67  func (c *batchChannel) processPendingUpdate(u *lnrpc.OpenStatusUpdate) error {
    68  	pendingUpd := u.GetChanPending()
    69  	if pendingUpd == nil {
    70  		return fmt.Errorf("got unexpected channel update %v", u.Update)
    71  	}
    72  
    73  	hash, err := chainhash.NewHash(pendingUpd.Txid)
    74  	if err != nil {
    75  		return fmt.Errorf("could not parse outpoint TX hash: %v", err)
    76  	}
    77  
    78  	c.chanPoint = &wire.OutPoint{
    79  		Index: pendingUpd.OutputIndex,
    80  		Hash:  *hash,
    81  	}
    82  	c.isPending = true
    83  
    84  	return nil
    85  }
    86  
    87  // RequestParser is a function that parses an incoming RPC request into the
    88  // internal funding initialization message.
    89  type RequestParser func(*lnrpc.OpenChannelRequest) (*InitFundingMsg, error)
    90  
    91  // ChannelOpener is a function that kicks off the initial channel open
    92  // negotiation with the peer.
    93  type ChannelOpener func(*InitFundingMsg) (chan *lnrpc.OpenStatusUpdate,
    94  	chan error)
    95  
    96  // ChannelAbandoner is a function that can abandon a channel in the local
    97  // database, graph and arbitrator state.
    98  type ChannelAbandoner func(*wire.OutPoint) error
    99  
   100  // WalletKitServer is a local interface that abstracts away the methods we need
   101  // from the wallet kit sub server instance.
   102  type WalletKitServer interface {
   103  	// FundPsbt creates a fully populated PSBT that contains enough inputs
   104  	// to fund the outputs specified in the template.
   105  	FundPsbt(context.Context,
   106  		*walletrpc.FundPsbtRequest) (*walletrpc.FundPsbtResponse, error)
   107  
   108  	// FinalizePsbt expects a partial transaction with all inputs and
   109  	// outputs fully declared and tries to sign all inputs that belong to
   110  	// the wallet.
   111  	FinalizePsbt(context.Context,
   112  		*walletrpc.FinalizePsbtRequest) (*walletrpc.FinalizePsbtResponse,
   113  		error)
   114  
   115  	// ReleaseOutput unlocks an output, allowing it to be available for coin
   116  	// selection if it remains unspent. The ID should match the one used to
   117  	// originally lock the output.
   118  	ReleaseOutput(context.Context,
   119  		*walletrpc.ReleaseOutputRequest) (*walletrpc.ReleaseOutputResponse,
   120  		error)
   121  }
   122  
   123  // Wallet is a local interface that abstracts away the methods we need from the
   124  // internal lightning wallet instance.
   125  type Wallet interface {
   126  	// PsbtFundingVerify looks up a previously registered funding intent by
   127  	// its pending channel ID and tries to advance the state machine by
   128  	// verifying the passed PSBT.
   129  	PsbtFundingVerify([32]byte, *psbt.Packet, bool) error
   130  
   131  	// PsbtFundingFinalize looks up a previously registered funding intent
   132  	// by its pending channel ID and tries to advance the state machine by
   133  	// finalizing the passed PSBT.
   134  	PsbtFundingFinalize([32]byte, *psbt.Packet, *wire.MsgTx) error
   135  
   136  	// PublishTransaction performs cursory validation (dust checks, etc),
   137  	// then finally broadcasts the passed transaction to the Bitcoin network.
   138  	PublishTransaction(*wire.MsgTx, string) error
   139  
   140  	// CancelFundingIntent allows a caller to cancel a previously registered
   141  	// funding intent. If no intent was found, then an error will be
   142  	// returned.
   143  	CancelFundingIntent([32]byte) error
   144  }
   145  
   146  // BatchConfig is the configuration for executing a single batch transaction for
   147  // opening multiple channels atomically.
   148  type BatchConfig struct {
   149  	// RequestParser is the function that parses an incoming RPC request
   150  	// into the internal funding initialization message.
   151  	RequestParser RequestParser
   152  
   153  	// ChannelOpener is the function that kicks off the initial channel open
   154  	// negotiation with the peer.
   155  	ChannelOpener ChannelOpener
   156  
   157  	// ChannelAbandoner is the function that can abandon a channel in the
   158  	// local database, graph and arbitrator state.
   159  	ChannelAbandoner ChannelAbandoner
   160  
   161  	// WalletKitServer is an instance of the wallet kit sub server that can
   162  	// handle PSBT funding and finalization.
   163  	WalletKitServer WalletKitServer
   164  
   165  	// Wallet is an instance of the internal lightning wallet.
   166  	Wallet Wallet
   167  
   168  	// NetParams contains the current bitcoin network parameters.
   169  	NetParams *chaincfg.Params
   170  
   171  	// Quit is the channel that is selected on to recognize if the main
   172  	// server is shutting down.
   173  	Quit chan struct{}
   174  }
   175  
   176  // Batcher is a type that can be used to perform an atomic funding of multiple
   177  // channels within a single on-chain transaction.
   178  type Batcher struct {
   179  	cfg *BatchConfig
   180  
   181  	channels    []*batchChannel
   182  	lockedUTXOs []*walletrpc.UtxoLease
   183  
   184  	didPublish bool
   185  }
   186  
   187  // NewBatcher returns a new batch channel funding helper.
   188  func NewBatcher(cfg *BatchConfig) *Batcher {
   189  	return &Batcher{
   190  		cfg: cfg,
   191  	}
   192  }
   193  
   194  // BatchFund starts the atomic batch channel funding process.
   195  //
   196  // NOTE: This method should only be called once per instance.
   197  func (b *Batcher) BatchFund(ctx context.Context,
   198  	req *lnrpc.BatchOpenChannelRequest) ([]*lnrpc.PendingUpdate, error) {
   199  
   200  	label, err := labels.ValidateAPI(req.Label)
   201  	if err != nil {
   202  		return nil, err
   203  	}
   204  
   205  	// Parse and validate each individual channel.
   206  	b.channels = make([]*batchChannel, 0, len(req.Channels))
   207  	for idx, rpcChannel := range req.Channels {
   208  		// If the user specifies a channel ID, it must be exactly 32
   209  		// bytes long.
   210  		if len(rpcChannel.PendingChanId) > 0 &&
   211  			len(rpcChannel.PendingChanId) != 32 {
   212  
   213  			return nil, fmt.Errorf("invalid temp chan ID %x",
   214  				rpcChannel.PendingChanId)
   215  		}
   216  
   217  		var pendingChanID [32]byte
   218  		if len(rpcChannel.PendingChanId) == 32 {
   219  			copy(pendingChanID[:], rpcChannel.PendingChanId)
   220  
   221  			// Don't allow the user to be clever by just setting an
   222  			// all zero channel ID, we need a "real" value here.
   223  			if pendingChanID == emptyChannelID {
   224  				return nil, fmt.Errorf("invalid empty temp " +
   225  					"chan ID")
   226  			}
   227  		} else if _, err := rand.Read(pendingChanID[:]); err != nil {
   228  			return nil, fmt.Errorf("error making temp chan ID: %v",
   229  				err)
   230  		}
   231  
   232  		fundingReq, err := b.cfg.RequestParser(&lnrpc.OpenChannelRequest{
   233  			AtomsPerByte:       req.AtomsPerByte,
   234  			NodePubkey:         rpcChannel.NodePubkey,
   235  			LocalFundingAmount: rpcChannel.LocalFundingAmount,
   236  			PushAtoms:          rpcChannel.PushAtoms,
   237  			TargetConf:         req.TargetConf,
   238  			Private:            rpcChannel.Private,
   239  			MinHtlcMAtoms:      rpcChannel.MinHtlcMAtoms,
   240  			RemoteCsvDelay:     rpcChannel.RemoteCsvDelay,
   241  			MinConfs:           req.MinConfs,
   242  			SpendUnconfirmed:   req.SpendUnconfirmed,
   243  			CloseAddress:       rpcChannel.CloseAddress,
   244  			CommitmentType:     rpcChannel.CommitmentType,
   245  			FundingShim: &lnrpc.FundingShim{
   246  				Shim: &lnrpc.FundingShim_PsbtShim{
   247  					PsbtShim: &lnrpc.PsbtShim{
   248  						PendingChanId: pendingChanID[:],
   249  						NoPublish:     true,
   250  					},
   251  				},
   252  			},
   253  		})
   254  		if err != nil {
   255  			return nil, fmt.Errorf("error parsing channel %d: %v",
   256  				idx, err)
   257  		}
   258  
   259  		// Prepare the stuff that we'll need for the internal PSBT
   260  		// funding.
   261  		fundingReq.PendingChanID = pendingChanID
   262  		fundingReq.ChanFunder = chanfunding.NewPsbtAssembler(
   263  			dcrutil.Amount(rpcChannel.LocalFundingAmount), nil,
   264  			b.cfg.NetParams, false,
   265  		)
   266  
   267  		b.channels = append(b.channels, &batchChannel{
   268  			pendingChanID: pendingChanID,
   269  			fundingReq:    fundingReq,
   270  		})
   271  	}
   272  
   273  	// From this point on we can fail for any of the channels and for any
   274  	// number of reasons. This deferred function makes sure that the full
   275  	// operation is actually atomic: We either succeed and publish a
   276  	// transaction for the full batch or we clean up everything.
   277  	defer b.cleanup(ctx)
   278  
   279  	// Now that we know the user input is sane, we need to kick off the
   280  	// channel funding negotiation with the peers. Because we specified a
   281  	// PSBT assembler, we'll get a special response in the channel once the
   282  	// funding output script is known (which we need to craft the TX).
   283  	eg := &errgroup.Group{}
   284  	for _, channel := range b.channels {
   285  		channel.updateChan, channel.errChan = b.cfg.ChannelOpener(
   286  			channel.fundingReq,
   287  		)
   288  
   289  		// Launch a goroutine that waits for the initial response on
   290  		// either the update or error chan.
   291  		channel := channel
   292  		eg.Go(func() error {
   293  			return b.waitForUpdate(channel, true)
   294  		})
   295  	}
   296  
   297  	// Wait for all goroutines to report back. Any error at this stage means
   298  	// we need to abort.
   299  	if err := eg.Wait(); err != nil {
   300  		return nil, fmt.Errorf("error batch opening channel, initial "+
   301  			"negotiation failed: %v", err)
   302  	}
   303  
   304  	// We can now assemble all outputs that we're going to give to the PSBT
   305  	// funding method of the wallet kit server.
   306  	txTemplate := &walletrpc.TxTemplate{
   307  		Outputs: make(map[string]uint64),
   308  	}
   309  	for _, channel := range b.channels {
   310  		txTemplate.Outputs[channel.fundingAddr] = uint64(
   311  			channel.fundingReq.LocalFundingAmt,
   312  		)
   313  	}
   314  
   315  	// Great, we've now started the channel negotiation successfully with
   316  	// all peers. This means we know the channel outputs for all channels
   317  	// and can craft our PSBT now. We take the fee rate and min conf
   318  	// settings from the first request as all of them should be equal
   319  	// anyway.
   320  	firstReq := b.channels[0].fundingReq
   321  	feeRateAtomPerKByte := firstReq.FundingFeePerKB
   322  	fundPsbtReq := &walletrpc.FundPsbtRequest{
   323  		Template: &walletrpc.FundPsbtRequest_Raw{
   324  			Raw: txTemplate,
   325  		},
   326  		Fees: &walletrpc.FundPsbtRequest_AtomsPerByte{
   327  			AtomsPerByte: uint64(feeRateAtomPerKByte) / 1000,
   328  		},
   329  		MinConfs:         firstReq.MinConfs,
   330  		SpendUnconfirmed: firstReq.MinConfs == 0,
   331  	}
   332  	fundPsbtResp, err := b.cfg.WalletKitServer.FundPsbt(ctx, fundPsbtReq)
   333  	if err != nil {
   334  		return nil, fmt.Errorf("error funding PSBT for batch channel "+
   335  			"open: %v", err)
   336  	}
   337  
   338  	// Funding was successful. This means there are some UTXOs that are now
   339  	// locked for us. We need to make sure we release them if we don't
   340  	// complete the publish process.
   341  	b.lockedUTXOs = fundPsbtResp.LockedUtxos
   342  
   343  	// Parse and log the funded PSBT for debugging purposes.
   344  	unsignedPacket, err := psbt.NewFromRawBytes(
   345  		bytes.NewReader(fundPsbtResp.FundedPsbt), false,
   346  	)
   347  	if err != nil {
   348  		return nil, fmt.Errorf("error parsing funded PSBT for batch "+
   349  			"channel open: %v", err)
   350  	}
   351  	log.Tracef("[batchopenchannel] funded PSBT: %s",
   352  		base64.StdEncoding.EncodeToString(fundPsbtResp.FundedPsbt))
   353  
   354  	// With the funded PSBT we can now advance the funding state machine of
   355  	// each of the channels.
   356  	for _, channel := range b.channels {
   357  		err = b.cfg.Wallet.PsbtFundingVerify(
   358  			channel.pendingChanID, unsignedPacket, false,
   359  		)
   360  		if err != nil {
   361  			return nil, fmt.Errorf("error verifying PSBT: %v", err)
   362  		}
   363  	}
   364  
   365  	// The funded PSBT was accepted by each of the assemblers, let's now
   366  	// sign/finalize it.
   367  	finalizePsbtResp, err := b.cfg.WalletKitServer.FinalizePsbt(
   368  		ctx, &walletrpc.FinalizePsbtRequest{
   369  			FundedPsbt: fundPsbtResp.FundedPsbt,
   370  		},
   371  	)
   372  	if err != nil {
   373  		return nil, fmt.Errorf("error finalizing PSBT for batch "+
   374  			"channel open: %v", err)
   375  	}
   376  	finalTx := &wire.MsgTx{}
   377  	txReader := bytes.NewReader(finalizePsbtResp.RawFinalTx)
   378  	if err := finalTx.Deserialize(txReader); err != nil {
   379  		return nil, fmt.Errorf("error parsing signed raw TX: %v", err)
   380  	}
   381  	log.Tracef("[batchopenchannel] signed PSBT: %s",
   382  		base64.StdEncoding.EncodeToString(finalizePsbtResp.SignedPsbt))
   383  
   384  	// Advance the funding state machine of each of the channels a last time
   385  	// to complete the negotiation with the now signed funding TX.
   386  	for _, channel := range b.channels {
   387  		err = b.cfg.Wallet.PsbtFundingFinalize(
   388  			channel.pendingChanID, nil, finalTx,
   389  		)
   390  		if err != nil {
   391  			return nil, fmt.Errorf("error finalizing PSBT: %v", err)
   392  		}
   393  	}
   394  
   395  	// Now every channel should be ready for the funding transaction to be
   396  	// broadcast. Let's wait for the updates that actually confirm this
   397  	// state.
   398  	eg = &errgroup.Group{}
   399  	for _, channel := range b.channels {
   400  		// Launch another goroutine that waits for the channel pending
   401  		// response on the update chan.
   402  		channel := channel
   403  		eg.Go(func() error {
   404  			return b.waitForUpdate(channel, false)
   405  		})
   406  	}
   407  
   408  	// Wait for all updates and make sure we're still good to proceed.
   409  	if err := eg.Wait(); err != nil {
   410  		return nil, fmt.Errorf("error batch opening channel, final "+
   411  			"negotiation failed: %v", err)
   412  	}
   413  
   414  	// Great, we're now finally ready to publish the transaction.
   415  	err = b.cfg.Wallet.PublishTransaction(finalTx, label)
   416  	if err != nil {
   417  		return nil, fmt.Errorf("error publishing final batch "+
   418  			"transaction: %v", err)
   419  	}
   420  	b.didPublish = true
   421  
   422  	rpcPoints := make([]*lnrpc.PendingUpdate, len(b.channels))
   423  	for idx, channel := range b.channels {
   424  		rpcPoints[idx] = &lnrpc.PendingUpdate{
   425  			Txid:        channel.chanPoint.Hash.CloneBytes(),
   426  			OutputIndex: channel.chanPoint.Index,
   427  		}
   428  	}
   429  
   430  	return rpcPoints, nil
   431  }
   432  
   433  // waitForUpdate waits for an incoming channel update (or error) for a single
   434  // channel.
   435  //
   436  // NOTE: Must be called in a goroutine as this blocks until an update or error
   437  // is received.
   438  func (b *Batcher) waitForUpdate(channel *batchChannel, firstUpdate bool) error {
   439  	select {
   440  	// If an error occurs then immediately return the error to the client.
   441  	case err := <-channel.errChan:
   442  		log.Errorf("unable to open channel to NodeKey(%x): %v",
   443  			channel.fundingReq.TargetPubkey.SerializeCompressed(),
   444  			err)
   445  		return err
   446  
   447  	// Otherwise, wait for the next channel update. The first update sent
   448  	// must be the signal to start the PSBT funding in our case since we
   449  	// specified a PSBT shim. The second update will be the signal that the
   450  	// channel is now pending.
   451  	case fundingUpdate := <-channel.updateChan:
   452  		log.Tracef("[batchopenchannel] received update: %v",
   453  			fundingUpdate)
   454  
   455  		// Depending on what update we were waiting for the batch
   456  		// channel knows what to do with it.
   457  		if firstUpdate {
   458  			return channel.processPsbtUpdate(fundingUpdate)
   459  		}
   460  
   461  		return channel.processPendingUpdate(fundingUpdate)
   462  
   463  	case <-b.cfg.Quit:
   464  		return errShuttingDown
   465  	}
   466  }
   467  
   468  // cleanup tries to remove any pending state or UTXO locks in case we had to
   469  // abort before finalizing and publishing the funding transaction.
   470  func (b *Batcher) cleanup(ctx context.Context) {
   471  	// Did we publish a transaction? Then there's nothing to clean up since
   472  	// we succeeded.
   473  	if b.didPublish {
   474  		return
   475  	}
   476  
   477  	// Make sure the error message doesn't sound too scary. These might be
   478  	// logged quite frequently depending on where exactly things were
   479  	// aborted. We could just not log any cleanup errors though it might be
   480  	// helpful to debug things if something doesn't go as expected.
   481  	const errMsgTpl = "Attempted to clean up after failed batch channel " +
   482  		"open but could not %s: %v"
   483  
   484  	// If we failed, we clean up in reverse order. First, let's unlock the
   485  	// leased outputs.
   486  	for _, lockedUTXO := range b.lockedUTXOs {
   487  		rpcOP := &lnrpc.OutPoint{
   488  			OutputIndex: lockedUTXO.Outpoint.OutputIndex,
   489  			TxidBytes:   lockedUTXO.Outpoint.TxidBytes,
   490  		}
   491  		_, err := b.cfg.WalletKitServer.ReleaseOutput(
   492  			ctx, &walletrpc.ReleaseOutputRequest{
   493  				Id:       lockedUTXO.Id,
   494  				Outpoint: rpcOP,
   495  			},
   496  		)
   497  		if err != nil {
   498  			log.Debugf(errMsgTpl, "release locked output "+
   499  				lockedUTXO.Outpoint.String(), err)
   500  		}
   501  	}
   502  
   503  	// Then go through all channels that ever got into a pending state and
   504  	// remove the pending channel by abandoning them.
   505  	for _, channel := range b.channels {
   506  		if !channel.isPending {
   507  			continue
   508  		}
   509  
   510  		err := b.cfg.ChannelAbandoner(channel.chanPoint)
   511  		if err != nil {
   512  			log.Debugf(errMsgTpl, "abandon pending open channel",
   513  				err)
   514  		}
   515  	}
   516  
   517  	// And finally clean up the funding shim for each channel that didn't
   518  	// make it into a pending state.
   519  	for _, channel := range b.channels {
   520  		if channel.isPending {
   521  			continue
   522  		}
   523  
   524  		err := b.cfg.Wallet.CancelFundingIntent(channel.pendingChanID)
   525  		if err != nil {
   526  			log.Debugf(errMsgTpl, "cancel funding shim", err)
   527  		}
   528  	}
   529  }