lnwallet: add ability to add extra co-op close outputs
This commit is contained in:
1 parent
7b396f4969
commit
517608ca3b
2 files changed
+460
-6
No files matched your search
+147
-6
@@ -7691,10 +7691,21 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel,
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CloseOutput wraps a normal tx out with additional metadata that indicates if
|
||||||
|
// the output belongs to the initiator of the channel or not.
|
||||||
|
type CloseOutput struct {
|
||||||
|
wire.TxOut
|
||||||
|
|
||||||
|
// IsLocal indicates if the output belong to the local party.
|
||||||
|
IsLocal bool
|
||||||
|
}
|
||||||
|
|
||||||
// chanCloseOpt is a functional option that can be used to modify the co-op
|
// chanCloseOpt is a functional option that can be used to modify the co-op
|
||||||
// close process.
|
// close process.
|
||||||
type chanCloseOpt struct {
|
type chanCloseOpt struct {
|
||||||
musigSession *MusigSession
|
musigSession *MusigSession
|
||||||
|
|
||||||
|
extraCloseOutputs []CloseOutput
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChanCloseOpt is a closure type that cen be used to modify the set of default
|
// ChanCloseOpt is a closure type that cen be used to modify the set of default
|
||||||
@@ -7715,6 +7726,14 @@ func WithCoopCloseMusigSession(session *MusigSession) ChanCloseOpt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithExtraCloseOutputs can be used to add extra outputs to the cooperative
|
||||||
|
// transaction.
|
||||||
|
func WithExtraCloseOutputs(extraOutputs []CloseOutput) ChanCloseOpt {
|
||||||
|
return func(opts *chanCloseOpt) {
|
||||||
|
opts.extraCloseOutputs = extraOutputs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// CreateCloseProposal is used by both parties in a cooperative channel close
|
// CreateCloseProposal is used by both parties in a cooperative channel close
|
||||||
// workflow to generate proposed close transactions and signatures. This method
|
// workflow to generate proposed close transactions and signatures. This method
|
||||||
// should only be executed once all pending HTLCs (if any) on the channel have
|
// should only be executed once all pending HTLCs (if any) on the channel have
|
||||||
@@ -7722,9 +7741,6 @@ func WithCoopCloseMusigSession(session *MusigSession) ChanCloseOpt {
|
|||||||
// the "closing" state, which indicates that all incoming/outgoing HTLC
|
// the "closing" state, which indicates that all incoming/outgoing HTLC
|
||||||
// requests should be rejected. A signature for the closing transaction is
|
// requests should be rejected. A signature for the closing transaction is
|
||||||
// returned.
|
// returned.
|
||||||
//
|
|
||||||
// TODO(roasbeef): caller should initiate signal to reject all incoming HTLCs,
|
|
||||||
// settle any in flight.
|
|
||||||
func (lc *LightningChannel) CreateCloseProposal(proposedFee btcutil.Amount,
|
func (lc *LightningChannel) CreateCloseProposal(proposedFee btcutil.Amount,
|
||||||
localDeliveryScript []byte, remoteDeliveryScript []byte,
|
localDeliveryScript []byte, remoteDeliveryScript []byte,
|
||||||
closeOpts ...ChanCloseOpt) (input.Signature, *chainhash.Hash,
|
closeOpts ...ChanCloseOpt) (input.Signature, *chainhash.Hash,
|
||||||
@@ -7735,7 +7751,6 @@ func (lc *LightningChannel) CreateCloseProposal(proposedFee btcutil.Amount,
|
|||||||
|
|
||||||
// If we're already closing the channel, then ignore this request.
|
// If we're already closing the channel, then ignore this request.
|
||||||
if lc.isClosed {
|
if lc.isClosed {
|
||||||
// TODO(roasbeef): check to ensure no pending payments
|
|
||||||
return nil, nil, 0, ErrChanClosing
|
return nil, nil, 0, ErrChanClosing
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7762,6 +7777,14 @@ func (lc *LightningChannel) CreateCloseProposal(proposedFee btcutil.Amount,
|
|||||||
closeTxOpts = append(closeTxOpts, WithRBFCloseTx())
|
closeTxOpts = append(closeTxOpts, WithRBFCloseTx())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we have any extra outputs to pass along, then we'll map that to
|
||||||
|
// the co-op close option txn type.
|
||||||
|
if opts.extraCloseOutputs != nil {
|
||||||
|
closeTxOpts = append(closeTxOpts, WithExtraTxCloseOutputs(
|
||||||
|
opts.extraCloseOutputs,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
closeTx := CreateCooperativeCloseTx(
|
closeTx := CreateCooperativeCloseTx(
|
||||||
fundingTxIn(lc.channelState), lc.channelState.LocalChanCfg.DustLimit,
|
fundingTxIn(lc.channelState), lc.channelState.LocalChanCfg.DustLimit,
|
||||||
lc.channelState.RemoteChanCfg.DustLimit, ourBalance, theirBalance,
|
lc.channelState.RemoteChanCfg.DustLimit, ourBalance, theirBalance,
|
||||||
@@ -7844,6 +7867,14 @@ func (lc *LightningChannel) CompleteCooperativeClose(
|
|||||||
closeTxOpts = append(closeTxOpts, WithRBFCloseTx())
|
closeTxOpts = append(closeTxOpts, WithRBFCloseTx())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we have any extra outputs to pass along, then we'll map that to
|
||||||
|
// the co-op close option txn type.
|
||||||
|
if opts.extraCloseOutputs != nil {
|
||||||
|
closeTxOpts = append(closeTxOpts, WithExtraTxCloseOutputs(
|
||||||
|
opts.extraCloseOutputs,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
// Create the transaction used to return the current settled balance
|
// Create the transaction used to return the current settled balance
|
||||||
// on this active channel back to both parties. In this current model,
|
// on this active channel back to both parties. In this current model,
|
||||||
// the initiator pays full fees for the cooperative close transaction.
|
// the initiator pays full fees for the cooperative close transaction.
|
||||||
@@ -8549,6 +8580,10 @@ type closeTxOpts struct {
|
|||||||
// enableRBF indicates whether the cooperative close tx should signal
|
// enableRBF indicates whether the cooperative close tx should signal
|
||||||
// RBF or not.
|
// RBF or not.
|
||||||
enableRBF bool
|
enableRBF bool
|
||||||
|
|
||||||
|
// extraCloseOutputs is a set of additional outputs that should be
|
||||||
|
// added the co-op close transaction.
|
||||||
|
extraCloseOutputs []CloseOutput
|
||||||
}
|
}
|
||||||
|
|
||||||
// defaultCloseTxOpts returns a closeTxOpts struct with default values.
|
// defaultCloseTxOpts returns a closeTxOpts struct with default values.
|
||||||
@@ -8569,6 +8604,14 @@ func WithRBFCloseTx() CloseTxOpt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithExtraTxCloseOutputs can be used to add extra outputs to the cooperative
|
||||||
|
// transaction.
|
||||||
|
func WithExtraTxCloseOutputs(extraOutputs []CloseOutput) CloseTxOpt {
|
||||||
|
return func(o *closeTxOpts) {
|
||||||
|
o.extraCloseOutputs = extraOutputs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// CreateCooperativeCloseTx creates a transaction which if signed by both
|
// CreateCooperativeCloseTx creates a transaction which if signed by both
|
||||||
// parties, then broadcast cooperatively closes an active channel. The creation
|
// parties, then broadcast cooperatively closes an active channel. The creation
|
||||||
// of the closure transaction is modified by a boolean indicating if the party
|
// of the closure transaction is modified by a boolean indicating if the party
|
||||||
@@ -8600,17 +8643,115 @@ func CreateCooperativeCloseTx(fundingTxIn wire.TxIn,
|
|||||||
|
|
||||||
// Create both cooperative closure outputs, properly respecting the
|
// Create both cooperative closure outputs, properly respecting the
|
||||||
// dust limits of both parties.
|
// dust limits of both parties.
|
||||||
if ourBalance >= localDust {
|
var localOutputIdx fn.Option[int]
|
||||||
|
haveLocalOutput := ourBalance >= localDust
|
||||||
|
if haveLocalOutput {
|
||||||
closeTx.AddTxOut(&wire.TxOut{
|
closeTx.AddTxOut(&wire.TxOut{
|
||||||
PkScript: ourDeliveryScript,
|
PkScript: ourDeliveryScript,
|
||||||
Value: int64(ourBalance),
|
Value: int64(ourBalance),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
localOutputIdx = fn.Some(len(closeTx.TxOut) - 1)
|
||||||
}
|
}
|
||||||
if theirBalance >= remoteDust {
|
|
||||||
|
var remoteOutputIdx fn.Option[int]
|
||||||
|
haveRemoteOutput := theirBalance >= remoteDust
|
||||||
|
if haveRemoteOutput {
|
||||||
closeTx.AddTxOut(&wire.TxOut{
|
closeTx.AddTxOut(&wire.TxOut{
|
||||||
PkScript: theirDeliveryScript,
|
PkScript: theirDeliveryScript,
|
||||||
Value: int64(theirBalance),
|
Value: int64(theirBalance),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
remoteOutputIdx = fn.Some(len(closeTx.TxOut) - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have extra outputs to add to the co-op close transaction, then
|
||||||
|
// we'll examine them now. We'll deduct the output's value from the
|
||||||
|
// owning party. In the case that a party can't pay for the output, then
|
||||||
|
// their normal output will be omitted.
|
||||||
|
for _, extraTxOut := range opts.extraCloseOutputs {
|
||||||
|
switch {
|
||||||
|
// For additional local outputs, add the output, then deduct
|
||||||
|
// the balance from our local balance.
|
||||||
|
case extraTxOut.IsLocal:
|
||||||
|
// The extraCloseOutputs in the options just indicate if
|
||||||
|
// an extra output should be added in general. But we
|
||||||
|
// only add one if we actually _need_ one, based on the
|
||||||
|
// balance. If we don't have enough local balance to
|
||||||
|
// cover the extra output, then localOutputIdx is None.
|
||||||
|
localOutputIdx.WhenSome(func(idx int) {
|
||||||
|
// The output that currently represents the
|
||||||
|
// local balance, which means:
|
||||||
|
// txOut.Value == ourBalance.
|
||||||
|
txOut := closeTx.TxOut[idx]
|
||||||
|
|
||||||
|
// The extra output (if one exists) is the more
|
||||||
|
// important one, as in custom channels it might
|
||||||
|
// carry some additional values. The normal
|
||||||
|
// output is just an address that sends the
|
||||||
|
// local balance back to our wallet. The extra
|
||||||
|
// one also goes to our wallet, but might also
|
||||||
|
// carry other values, so it has higher
|
||||||
|
// priority. Do we have enough balance to have
|
||||||
|
// both the extra output with the given value
|
||||||
|
// (which is subtracted from our balance) and
|
||||||
|
// still an above-dust normal output? If not, we
|
||||||
|
// skip the extra output and just overwrite the
|
||||||
|
// existing output script with the one from the
|
||||||
|
// extra output.
|
||||||
|
amtAfterOutput := btcutil.Amount(
|
||||||
|
txOut.Value - extraTxOut.Value,
|
||||||
|
)
|
||||||
|
if amtAfterOutput <= localDust {
|
||||||
|
txOut.PkScript = extraTxOut.PkScript
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
txOut.Value -= extraTxOut.Value
|
||||||
|
closeTx.AddTxOut(&extraTxOut.TxOut)
|
||||||
|
})
|
||||||
|
|
||||||
|
// For extra remote outputs, we'll do the opposite.
|
||||||
|
case !extraTxOut.IsLocal:
|
||||||
|
// The extraCloseOutputs in the options just indicate if
|
||||||
|
// an extra output should be added in general. But we
|
||||||
|
// only add one if we actually _need_ one, based on the
|
||||||
|
// balance. If we don't have enough remote balance to
|
||||||
|
// cover the extra output, then remoteOutputIdx is None.
|
||||||
|
remoteOutputIdx.WhenSome(func(idx int) {
|
||||||
|
// The output that currently represents the
|
||||||
|
// remote balance, which means:
|
||||||
|
// txOut.Value == theirBalance.
|
||||||
|
txOut := closeTx.TxOut[idx]
|
||||||
|
|
||||||
|
// The extra output (if one exists) is the more
|
||||||
|
// important one, as in custom channels it might
|
||||||
|
// carry some additional values. The normal
|
||||||
|
// output is just an address that sends the
|
||||||
|
// remote balance back to their wallet. The
|
||||||
|
// extra one also goes to their wallet, but
|
||||||
|
// might also carry other values, so it has
|
||||||
|
// higher priority. Do they have enough balance
|
||||||
|
// to have both the extra output with the given
|
||||||
|
// value (which is subtracted from their
|
||||||
|
// balance) and still an above-dust normal
|
||||||
|
// output? If not, we skip the extra output and
|
||||||
|
// just overwrite the existing output script
|
||||||
|
// with the one from the extra output.
|
||||||
|
amtAfterOutput := btcutil.Amount(
|
||||||
|
txOut.Value - extraTxOut.Value,
|
||||||
|
)
|
||||||
|
if amtAfterOutput <= remoteDust {
|
||||||
|
txOut.PkScript = extraTxOut.PkScript
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
txOut.Value -= extraTxOut.Value
|
||||||
|
closeTx.AddTxOut(&extraTxOut.TxOut)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
txsort.InPlaceSort(closeTx)
|
txsort.InPlaceSort(closeTx)
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/btcsuite/btcd/btcec/v2"
|
"github.com/btcsuite/btcd/btcec/v2"
|
||||||
"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
|
"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
|
||||||
"github.com/btcsuite/btcd/btcutil"
|
"github.com/btcsuite/btcd/btcutil"
|
||||||
|
"github.com/btcsuite/btcd/btcutil/txsort"
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/btcsuite/btcd/txscript"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
@@ -11476,3 +11477,315 @@ func TestBlindingPointPersistence(t *testing.T) {
|
|||||||
require.Equal(t, blinding,
|
require.Equal(t, blinding,
|
||||||
bobCommit.incomingHTLCs[0].BlindingPoint.UnwrapOrFailV(t))
|
bobCommit.incomingHTLCs[0].BlindingPoint.UnwrapOrFailV(t))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestCreateCooperativeCloseTx tests that the cooperative close transaction is
|
||||||
|
// properly created based on the standard and also optional parameters.
|
||||||
|
func TestCreateCooperativeCloseTx(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
fundingTxIn := &wire.TxIn{}
|
||||||
|
|
||||||
|
localDust := btcutil.Amount(400)
|
||||||
|
remoteDust := btcutil.Amount(400)
|
||||||
|
|
||||||
|
localScript := []byte{0}
|
||||||
|
localExtraScript := []byte{2}
|
||||||
|
|
||||||
|
remoteScript := []byte{1}
|
||||||
|
remoteExtraScript := []byte{3}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
|
||||||
|
enableRBF bool
|
||||||
|
|
||||||
|
localBalance btcutil.Amount
|
||||||
|
remoteBalance btcutil.Amount
|
||||||
|
|
||||||
|
extraCloseOutputs []CloseOutput
|
||||||
|
|
||||||
|
expectedTx *wire.MsgTx
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "no dust, no extra outputs",
|
||||||
|
localBalance: 1_000,
|
||||||
|
remoteBalance: 1_000,
|
||||||
|
expectedTx: &wire.MsgTx{
|
||||||
|
TxIn: []*wire.TxIn{
|
||||||
|
fundingTxIn,
|
||||||
|
},
|
||||||
|
Version: 2,
|
||||||
|
TxOut: []*wire.TxOut{
|
||||||
|
{
|
||||||
|
Value: 1_000,
|
||||||
|
PkScript: localScript,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: 1_000,
|
||||||
|
PkScript: remoteScript,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "local dust, no extra outputs",
|
||||||
|
localBalance: 100,
|
||||||
|
remoteBalance: 1_000,
|
||||||
|
expectedTx: &wire.MsgTx{
|
||||||
|
TxIn: []*wire.TxIn{
|
||||||
|
fundingTxIn,
|
||||||
|
},
|
||||||
|
Version: 2,
|
||||||
|
TxOut: []*wire.TxOut{
|
||||||
|
{
|
||||||
|
Value: 1_000,
|
||||||
|
PkScript: remoteScript,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "remote dust, no extra outputs",
|
||||||
|
localBalance: 1_000,
|
||||||
|
remoteBalance: 100,
|
||||||
|
expectedTx: &wire.MsgTx{
|
||||||
|
TxIn: []*wire.TxIn{
|
||||||
|
fundingTxIn,
|
||||||
|
},
|
||||||
|
Version: 2,
|
||||||
|
TxOut: []*wire.TxOut{
|
||||||
|
{
|
||||||
|
Value: 1_000,
|
||||||
|
PkScript: localScript,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no dust, local extra output",
|
||||||
|
localBalance: 10_000,
|
||||||
|
remoteBalance: 10_000,
|
||||||
|
extraCloseOutputs: []CloseOutput{
|
||||||
|
{
|
||||||
|
TxOut: wire.TxOut{
|
||||||
|
Value: 1_000,
|
||||||
|
PkScript: localExtraScript,
|
||||||
|
},
|
||||||
|
IsLocal: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedTx: &wire.MsgTx{
|
||||||
|
TxIn: []*wire.TxIn{
|
||||||
|
fundingTxIn,
|
||||||
|
},
|
||||||
|
Version: 2,
|
||||||
|
TxOut: []*wire.TxOut{
|
||||||
|
{
|
||||||
|
Value: 10_000,
|
||||||
|
PkScript: remoteScript,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: 9_000,
|
||||||
|
PkScript: localScript,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: 1_000,
|
||||||
|
PkScript: localExtraScript,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no dust, remote extra output",
|
||||||
|
localBalance: 10_000,
|
||||||
|
remoteBalance: 10_000,
|
||||||
|
extraCloseOutputs: []CloseOutput{
|
||||||
|
{
|
||||||
|
TxOut: wire.TxOut{
|
||||||
|
Value: 1_000,
|
||||||
|
PkScript: remoteExtraScript,
|
||||||
|
},
|
||||||
|
IsLocal: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedTx: &wire.MsgTx{
|
||||||
|
TxIn: []*wire.TxIn{
|
||||||
|
fundingTxIn,
|
||||||
|
},
|
||||||
|
Version: 2,
|
||||||
|
TxOut: []*wire.TxOut{
|
||||||
|
{
|
||||||
|
Value: 10_000,
|
||||||
|
PkScript: localScript,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: 9_000,
|
||||||
|
PkScript: remoteScript,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: 1_000,
|
||||||
|
PkScript: remoteExtraScript,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no dust, local+remote extra output",
|
||||||
|
localBalance: 10_000,
|
||||||
|
remoteBalance: 10_000,
|
||||||
|
extraCloseOutputs: []CloseOutput{
|
||||||
|
{
|
||||||
|
TxOut: wire.TxOut{
|
||||||
|
Value: 1_000,
|
||||||
|
PkScript: remoteExtraScript,
|
||||||
|
},
|
||||||
|
IsLocal: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
TxOut: wire.TxOut{
|
||||||
|
Value: 1_000,
|
||||||
|
PkScript: localExtraScript,
|
||||||
|
},
|
||||||
|
IsLocal: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedTx: &wire.MsgTx{
|
||||||
|
TxIn: []*wire.TxIn{
|
||||||
|
fundingTxIn,
|
||||||
|
},
|
||||||
|
Version: 2,
|
||||||
|
TxOut: []*wire.TxOut{
|
||||||
|
{
|
||||||
|
Value: 9_000,
|
||||||
|
PkScript: localScript,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: 9_000,
|
||||||
|
PkScript: remoteScript,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: 1_000,
|
||||||
|
PkScript: remoteExtraScript,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: 1_000,
|
||||||
|
PkScript: localExtraScript,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no dust, local+remote extra output, " +
|
||||||
|
"remote can't afford",
|
||||||
|
localBalance: 10_000,
|
||||||
|
remoteBalance: 1_000,
|
||||||
|
extraCloseOutputs: []CloseOutput{
|
||||||
|
{
|
||||||
|
TxOut: wire.TxOut{
|
||||||
|
Value: 1_000,
|
||||||
|
PkScript: remoteExtraScript,
|
||||||
|
},
|
||||||
|
IsLocal: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
TxOut: wire.TxOut{
|
||||||
|
Value: 1_000,
|
||||||
|
PkScript: localExtraScript,
|
||||||
|
},
|
||||||
|
IsLocal: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedTx: &wire.MsgTx{
|
||||||
|
TxIn: []*wire.TxIn{
|
||||||
|
fundingTxIn,
|
||||||
|
},
|
||||||
|
Version: 2,
|
||||||
|
TxOut: []*wire.TxOut{
|
||||||
|
{
|
||||||
|
Value: 9_000,
|
||||||
|
PkScript: localScript,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: 1_000,
|
||||||
|
PkScript: remoteExtraScript,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: 1_000,
|
||||||
|
PkScript: localExtraScript,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no dust, local+remote extra output, " +
|
||||||
|
"local can't afford",
|
||||||
|
localBalance: 1_000,
|
||||||
|
remoteBalance: 10_000,
|
||||||
|
extraCloseOutputs: []CloseOutput{
|
||||||
|
{
|
||||||
|
TxOut: wire.TxOut{
|
||||||
|
Value: 1_000,
|
||||||
|
PkScript: remoteExtraScript,
|
||||||
|
},
|
||||||
|
IsLocal: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
TxOut: wire.TxOut{
|
||||||
|
Value: 1_000,
|
||||||
|
PkScript: localExtraScript,
|
||||||
|
},
|
||||||
|
IsLocal: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedTx: &wire.MsgTx{
|
||||||
|
TxIn: []*wire.TxIn{
|
||||||
|
fundingTxIn,
|
||||||
|
},
|
||||||
|
Version: 2,
|
||||||
|
TxOut: []*wire.TxOut{
|
||||||
|
{
|
||||||
|
Value: 9_000,
|
||||||
|
PkScript: remoteScript,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: 1_000,
|
||||||
|
PkScript: remoteExtraScript,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: 1_000,
|
||||||
|
PkScript: localExtraScript,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
var opts []CloseTxOpt
|
||||||
|
if test.extraCloseOutputs != nil {
|
||||||
|
opts = append(
|
||||||
|
opts,
|
||||||
|
WithExtraTxCloseOutputs(
|
||||||
|
test.extraCloseOutputs,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
closeTx := CreateCooperativeCloseTx(
|
||||||
|
*fundingTxIn, localDust, remoteDust,
|
||||||
|
test.localBalance, test.remoteBalance,
|
||||||
|
localScript, remoteScript, opts...,
|
||||||
|
)
|
||||||
|
|
||||||
|
txsort.InPlaceSort(test.expectedTx)
|
||||||
|
|
||||||
|
require.Equal(
|
||||||
|
t, test.expectedTx, closeTx,
|
||||||
|
"expected %v, got %v",
|
||||||
|
spew.Sdump(test.expectedTx),
|
||||||
|
spew.Sdump(closeTx),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user