mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 09:53:54 +01:00
676 lines
19 KiB
Go
676 lines
19 KiB
Go
package sweep
|
|
|
|
import (
|
|
"errors"
|
|
"math"
|
|
"testing"
|
|
|
|
"github.com/btcsuite/btcd/btcutil"
|
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
"github.com/btcsuite/btcd/wire"
|
|
"github.com/lightningnetwork/lnd/fn"
|
|
"github.com/lightningnetwork/lnd/input"
|
|
"github.com/lightningnetwork/lnd/lnwallet"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// TestTxInputSet tests adding various sized inputs to the set.
|
|
func TestTxInputSet(t *testing.T) {
|
|
const (
|
|
feeRate = 1000
|
|
maxInputs = 10
|
|
)
|
|
set := newTxInputSet(feeRate, 0, maxInputs)
|
|
|
|
// Create a 300 sat input. The fee to sweep this input to a P2WKH output
|
|
// is 439 sats. That means that this input yields -139 sats and we
|
|
// expect it not to be added.
|
|
if set.add(createP2WKHInput(300), constraintsRegular) {
|
|
t.Fatal("expected add of negatively yielding input to fail")
|
|
}
|
|
|
|
// A 700 sat input should be accepted into the set, because it yields
|
|
// positively.
|
|
if !set.add(createP2WKHInput(700), constraintsRegular) {
|
|
t.Fatal("expected add of positively yielding input to succeed")
|
|
}
|
|
|
|
fee := set.weightEstimate(true).feeWithParent()
|
|
require.Equal(t, btcutil.Amount(487), fee)
|
|
|
|
// The tx output should now be 700-487 = 213 sats. The dust limit isn't
|
|
// reached yet.
|
|
if set.totalOutput() != 213 {
|
|
t.Fatal("unexpected output value")
|
|
}
|
|
if set.enoughInput() {
|
|
t.Fatal("expected dust limit not yet to be reached")
|
|
}
|
|
|
|
// Add a 1000 sat input. This increases the tx fee to 760 sats. The tx
|
|
// output should now be 1000+700 - 760 = 940 sats.
|
|
if !set.add(createP2WKHInput(1000), constraintsRegular) {
|
|
t.Fatal("expected add of positively yielding input to succeed")
|
|
}
|
|
if set.totalOutput() != 940 {
|
|
t.Fatal("unexpected output value")
|
|
}
|
|
if !set.enoughInput() {
|
|
t.Fatal("expected dust limit to be reached")
|
|
}
|
|
}
|
|
|
|
// TestTxInputSetFromWallet tests adding a wallet input to a TxInputSet to reach
|
|
// the dust limit.
|
|
func TestTxInputSetFromWallet(t *testing.T) {
|
|
const (
|
|
feeRate = 500
|
|
maxInputs = 10
|
|
)
|
|
|
|
wallet := &mockWallet{}
|
|
set := newTxInputSet(feeRate, 0, maxInputs)
|
|
|
|
// Add a 500 sat input to the set. It yields positively, but doesn't
|
|
// reach the output dust limit.
|
|
if !set.add(createP2WKHInput(500), constraintsRegular) {
|
|
t.Fatal("expected add of positively yielding input to succeed")
|
|
}
|
|
if set.enoughInput() {
|
|
t.Fatal("expected dust limit not yet to be reached")
|
|
}
|
|
|
|
// Expect that adding a negative yield input fails.
|
|
if set.add(createP2WKHInput(50), constraintsRegular) {
|
|
t.Fatal("expected negative yield input add to fail")
|
|
}
|
|
|
|
// Force add the negative yield input. It should succeed.
|
|
if !set.add(createP2WKHInput(50), constraintsForce) {
|
|
t.Fatal("expected forced add to succeed")
|
|
}
|
|
|
|
err := set.AddWalletInputs(wallet)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !set.enoughInput() {
|
|
t.Fatal("expected dust limit to be reached")
|
|
}
|
|
}
|
|
|
|
// createP2WKHInput returns a P2WKH test input with the specified amount.
|
|
func createP2WKHInput(amt btcutil.Amount) input.Input {
|
|
input := createTestInput(int64(amt), input.WitnessKeyHash)
|
|
return &input
|
|
}
|
|
|
|
type mockWallet struct {
|
|
Wallet
|
|
}
|
|
|
|
func (m *mockWallet) ListUnspentWitnessFromDefaultAccount(minConfs, maxConfs int32) (
|
|
[]*lnwallet.Utxo, error) {
|
|
|
|
return []*lnwallet.Utxo{
|
|
{
|
|
AddressType: lnwallet.WitnessPubKey,
|
|
Value: 10000,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
type reqInput struct {
|
|
input.Input
|
|
|
|
txOut *wire.TxOut
|
|
}
|
|
|
|
func (r *reqInput) RequiredTxOut() *wire.TxOut {
|
|
return r.txOut
|
|
}
|
|
|
|
// TestTxInputSetRequiredOutput tests that the tx input set behaves as expected
|
|
// when we add inputs that have required tx outs.
|
|
func TestTxInputSetRequiredOutput(t *testing.T) {
|
|
const (
|
|
feeRate = 1000
|
|
maxInputs = 10
|
|
)
|
|
set := newTxInputSet(feeRate, 0, maxInputs)
|
|
|
|
// Attempt to add an input with a required txout below the dust limit.
|
|
// This should fail since we cannot trim such outputs.
|
|
inp := &reqInput{
|
|
Input: createP2WKHInput(500),
|
|
txOut: &wire.TxOut{
|
|
Value: 500,
|
|
PkScript: make([]byte, input.P2PKHSize),
|
|
},
|
|
}
|
|
require.False(t, set.add(inp, constraintsRegular),
|
|
"expected adding dust required tx out to fail")
|
|
|
|
// Create a 1000 sat input that also has a required TxOut of 1000 sat.
|
|
// The fee to sweep this input to a P2WKH output is 439 sats.
|
|
inp = &reqInput{
|
|
Input: createP2WKHInput(1000),
|
|
txOut: &wire.TxOut{
|
|
Value: 1000,
|
|
PkScript: make([]byte, input.P2WPKHSize),
|
|
},
|
|
}
|
|
require.True(t, set.add(inp, constraintsRegular), "failed adding input")
|
|
|
|
// The fee needed to pay for this input and output should be 439 sats.
|
|
fee := set.weightEstimate(false).feeWithParent()
|
|
require.Equal(t, btcutil.Amount(439), fee)
|
|
|
|
// Since the tx set currently pays no fees, we expect the current
|
|
// change to actually be negative, since this is what it would cost us
|
|
// in fees to add a change output.
|
|
feeWithChange := set.weightEstimate(true).feeWithParent()
|
|
if set.changeOutput != -feeWithChange {
|
|
t.Fatalf("expected negative change of %v, had %v",
|
|
-feeWithChange, set.changeOutput)
|
|
}
|
|
|
|
// This should also be reflected by not having enough input.
|
|
require.False(t, set.enoughInput())
|
|
|
|
// Get a weight estimate without change output, and add an additional
|
|
// input to it.
|
|
dummyInput := createP2WKHInput(1000)
|
|
weight := set.weightEstimate(false)
|
|
require.NoError(t, weight.add(dummyInput))
|
|
|
|
// Now we add a an input that is large enough to pay the fee for the
|
|
// transaction without a change output, but not large enough to afford
|
|
// adding a change output.
|
|
extraInput1 := weight.feeWithParent() + 100
|
|
require.True(t, set.add(
|
|
createP2WKHInput(extraInput1), constraintsRegular,
|
|
), "expected add of positively yielding input to succeed")
|
|
|
|
// The change should be negative, since we would have to add a change
|
|
// output, which we cannot yet afford.
|
|
if set.changeOutput >= 0 {
|
|
t.Fatal("expected change to be negaitve")
|
|
}
|
|
|
|
// Even though we cannot afford a change output, the tx set is valid,
|
|
// since we can pay the fees without the change output.
|
|
require.True(t, set.enoughInput())
|
|
|
|
// Get another weight estimate, this time with a change output, and
|
|
// figure out how much we must add to afford a change output.
|
|
weight = set.weightEstimate(true)
|
|
require.NoError(t, weight.add(dummyInput))
|
|
|
|
// We add what is left to reach this value.
|
|
extraInput2 := weight.feeWithParent() - extraInput1 + 100
|
|
|
|
// Add this input, which should result in the change now being 100 sats.
|
|
require.True(t, set.add(
|
|
createP2WKHInput(extraInput2), constraintsRegular,
|
|
))
|
|
|
|
// The change should be 100, since this is what is left after paying
|
|
// fees in case of a change output.
|
|
change := set.changeOutput
|
|
if change != 100 {
|
|
t.Fatalf("expected change be 100, was %v", change)
|
|
}
|
|
|
|
// Even though the change output is dust, we have enough for fees, and
|
|
// we have an output, so it should be considered enough to craft a
|
|
// valid sweep transaction.
|
|
require.True(t, set.enoughInput())
|
|
|
|
// Finally we add an input that should push the change output above the
|
|
// dust limit.
|
|
weight = set.weightEstimate(true)
|
|
require.NoError(t, weight.add(dummyInput))
|
|
|
|
// We expect the change to everything that is left after paying the tx
|
|
// fee.
|
|
extraInput3 := weight.feeWithParent() - extraInput1 - extraInput2 + 1000
|
|
require.True(t, set.add(createP2WKHInput(extraInput3), constraintsRegular))
|
|
|
|
change = set.changeOutput
|
|
if change != 1000 {
|
|
t.Fatalf("expected change to be %v, had %v", 1000, change)
|
|
}
|
|
require.True(t, set.enoughInput())
|
|
}
|
|
|
|
// TestNewBudgetInputSet checks `NewBudgetInputSet` correctly validates the
|
|
// supplied inputs and returns the error.
|
|
func TestNewBudgetInputSet(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
rt := require.New(t)
|
|
|
|
// Pass an empty slice and expect an error.
|
|
set, err := NewBudgetInputSet([]SweeperInput{})
|
|
rt.ErrorContains(err, "inputs slice is empty")
|
|
rt.Nil(set)
|
|
|
|
// Create two inputs with different deadline heights.
|
|
inp0 := createP2WKHInput(1000)
|
|
inp1 := createP2WKHInput(1000)
|
|
inp2 := createP2WKHInput(1000)
|
|
input0 := SweeperInput{
|
|
Input: inp0,
|
|
params: Params{
|
|
Budget: 100,
|
|
DeadlineHeight: fn.None[int32](),
|
|
},
|
|
}
|
|
input1 := SweeperInput{
|
|
Input: inp1,
|
|
params: Params{
|
|
Budget: 100,
|
|
DeadlineHeight: fn.Some(int32(1)),
|
|
},
|
|
}
|
|
input2 := SweeperInput{
|
|
Input: inp2,
|
|
params: Params{
|
|
Budget: 100,
|
|
DeadlineHeight: fn.Some(int32(2)),
|
|
},
|
|
}
|
|
|
|
// Pass a slice of inputs with different deadline heights.
|
|
set, err = NewBudgetInputSet([]SweeperInput{input1, input2})
|
|
rt.ErrorContains(err, "inputs have different deadline heights")
|
|
rt.Nil(set)
|
|
|
|
// Pass a slice of inputs that only one input has the deadline height.
|
|
set, err = NewBudgetInputSet([]SweeperInput{input0, input2})
|
|
rt.NoError(err)
|
|
rt.NotNil(set)
|
|
|
|
// Pass a slice of inputs that are duplicates.
|
|
set, err = NewBudgetInputSet([]SweeperInput{input1, input1})
|
|
rt.ErrorContains(err, "duplicate inputs")
|
|
rt.Nil(set)
|
|
}
|
|
|
|
// TestBudgetInputSetAddInput checks that `addInput` correctly updates the
|
|
// budget of the input set.
|
|
func TestBudgetInputSetAddInput(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Create a testing input with a budget of 100 satoshis.
|
|
input := createP2WKHInput(1000)
|
|
pi := &SweeperInput{
|
|
Input: input,
|
|
params: Params{
|
|
Budget: 100,
|
|
},
|
|
}
|
|
|
|
// Initialize an input set, which adds the above input.
|
|
set, err := NewBudgetInputSet([]SweeperInput{*pi})
|
|
require.NoError(t, err)
|
|
|
|
// Add the input to the set again.
|
|
set.addInput(*pi)
|
|
|
|
// The set should now have two inputs.
|
|
require.Len(t, set.inputs, 2)
|
|
require.Equal(t, pi, set.inputs[0])
|
|
require.Equal(t, pi, set.inputs[1])
|
|
|
|
// The set should have a budget of 200 satoshis.
|
|
require.Equal(t, btcutil.Amount(200), set.Budget())
|
|
}
|
|
|
|
// TestNeedWalletInput checks that NeedWalletInput correctly determines if a
|
|
// wallet input is needed.
|
|
func TestNeedWalletInput(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Create a mock input that doesn't have required outputs.
|
|
mockInput := &input.MockInput{}
|
|
mockInput.On("RequiredTxOut").Return(nil)
|
|
defer mockInput.AssertExpectations(t)
|
|
|
|
// Create a mock input that has required outputs.
|
|
mockInputRequireOutput := &input.MockInput{}
|
|
mockInputRequireOutput.On("RequiredTxOut").Return(&wire.TxOut{})
|
|
defer mockInputRequireOutput.AssertExpectations(t)
|
|
|
|
// We now create two pending inputs each has a budget of 100 satoshis.
|
|
const budget = 100
|
|
|
|
// Create the pending input that doesn't have a required output.
|
|
piBudget := &SweeperInput{
|
|
Input: mockInput,
|
|
params: Params{Budget: budget},
|
|
}
|
|
|
|
// Create the pending input that has a required output.
|
|
piRequireOutput := &SweeperInput{
|
|
Input: mockInputRequireOutput,
|
|
params: Params{Budget: budget},
|
|
}
|
|
|
|
testCases := []struct {
|
|
name string
|
|
setupInputs func() []*SweeperInput
|
|
need bool
|
|
}{
|
|
{
|
|
// When there are no pending inputs, we won't need a
|
|
// wallet input. Technically this should be an invalid
|
|
// state.
|
|
name: "no inputs",
|
|
setupInputs: func() []*SweeperInput {
|
|
return nil
|
|
},
|
|
need: false,
|
|
},
|
|
{
|
|
// When there's no required output, we don't need a
|
|
// wallet input.
|
|
name: "no required outputs",
|
|
setupInputs: func() []*SweeperInput {
|
|
// Create a sign descriptor to be used in the
|
|
// pending input when calculating budgets can
|
|
// be borrowed.
|
|
sd := &input.SignDescriptor{
|
|
Output: &wire.TxOut{
|
|
Value: budget,
|
|
},
|
|
}
|
|
mockInput.On("SignDesc").Return(sd).Once()
|
|
|
|
return []*SweeperInput{piBudget}
|
|
},
|
|
need: false,
|
|
},
|
|
{
|
|
// When the output value cannot cover the budget, we
|
|
// need a wallet input.
|
|
name: "output value cannot cover budget",
|
|
setupInputs: func() []*SweeperInput {
|
|
// Create a sign descriptor to be used in the
|
|
// pending input when calculating budgets can
|
|
// be borrowed.
|
|
sd := &input.SignDescriptor{
|
|
Output: &wire.TxOut{
|
|
Value: budget - 1,
|
|
},
|
|
}
|
|
mockInput.On("SignDesc").Return(sd).Once()
|
|
|
|
// These two methods are only invoked when the
|
|
// unit test is running with a logger.
|
|
mockInput.On("OutPoint").Return(
|
|
wire.OutPoint{Hash: chainhash.Hash{1}},
|
|
).Maybe()
|
|
mockInput.On("WitnessType").Return(
|
|
input.CommitmentAnchor,
|
|
).Maybe()
|
|
|
|
return []*SweeperInput{piBudget}
|
|
},
|
|
need: true,
|
|
},
|
|
{
|
|
// When there's only inputs that require outputs, we
|
|
// need wallet inputs.
|
|
name: "only required outputs",
|
|
setupInputs: func() []*SweeperInput {
|
|
return []*SweeperInput{piRequireOutput}
|
|
},
|
|
need: true,
|
|
},
|
|
{
|
|
// When there's a mix of inputs, but the borrowable
|
|
// budget cannot cover the required, we need a wallet
|
|
// input.
|
|
name: "not enough budget to be borrowed",
|
|
setupInputs: func() []*SweeperInput {
|
|
// Create a sign descriptor to be used in the
|
|
// pending input when calculating budgets can
|
|
// be borrowed.
|
|
//
|
|
// NOTE: the value is exactly the same as the
|
|
// budget so we can't borrow any more.
|
|
sd := &input.SignDescriptor{
|
|
Output: &wire.TxOut{
|
|
Value: budget,
|
|
},
|
|
}
|
|
mockInput.On("SignDesc").Return(sd).Once()
|
|
|
|
return []*SweeperInput{
|
|
piBudget, piRequireOutput,
|
|
}
|
|
},
|
|
need: true,
|
|
},
|
|
{
|
|
// When there's a mix of inputs, and the budget can be
|
|
// borrowed covers the required, we don't need wallet
|
|
// inputs.
|
|
name: "enough budget to be borrowed",
|
|
setupInputs: func() []*SweeperInput {
|
|
// Create a sign descriptor to be used in the
|
|
// pending input when calculating budgets can
|
|
// be borrowed.
|
|
//
|
|
// NOTE: the value is exactly the same as the
|
|
// budget so we can't borrow any more.
|
|
sd := &input.SignDescriptor{
|
|
Output: &wire.TxOut{
|
|
Value: budget * 2,
|
|
},
|
|
}
|
|
mockInput.On("SignDesc").Return(sd).Once()
|
|
piBudget.Input = mockInput
|
|
|
|
return []*SweeperInput{
|
|
piBudget, piRequireOutput,
|
|
}
|
|
},
|
|
need: false,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
// Setup testing inputs.
|
|
inputs := tc.setupInputs()
|
|
|
|
// Initialize an input set, which adds the testing
|
|
// inputs.
|
|
set := &BudgetInputSet{inputs: inputs}
|
|
|
|
result := set.NeedWalletInput()
|
|
require.Equal(t, tc.need, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestAddWalletInputReturnErr tests the three possible errors returned from
|
|
// AddWalletInputs:
|
|
// - error from ListUnspentWitnessFromDefaultAccount.
|
|
// - error from createWalletTxInput.
|
|
// - error when wallet doesn't have utxos.
|
|
func TestAddWalletInputReturnErr(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
wallet := &MockWallet{}
|
|
defer wallet.AssertExpectations(t)
|
|
|
|
// Initialize an empty input set.
|
|
set := &BudgetInputSet{}
|
|
|
|
// Specify the min and max confs used in
|
|
// ListUnspentWitnessFromDefaultAccount.
|
|
min, max := int32(1), int32(math.MaxInt32)
|
|
|
|
// Mock the wallet to return an error.
|
|
dummyErr := errors.New("dummy error")
|
|
wallet.On("ListUnspentWitnessFromDefaultAccount",
|
|
min, max).Return(nil, dummyErr).Once()
|
|
|
|
// Check that the error is returned from
|
|
// ListUnspentWitnessFromDefaultAccount.
|
|
err := set.AddWalletInputs(wallet)
|
|
require.ErrorIs(t, err, dummyErr)
|
|
|
|
// Create an utxo with unknown address type to trigger an error.
|
|
utxo := &lnwallet.Utxo{
|
|
AddressType: lnwallet.UnknownAddressType,
|
|
}
|
|
|
|
// Mock the wallet to return the above utxo.
|
|
wallet.On("ListUnspentWitnessFromDefaultAccount",
|
|
min, max).Return([]*lnwallet.Utxo{utxo}, nil).Once()
|
|
|
|
// Check that the error is returned from createWalletTxInput.
|
|
err = set.AddWalletInputs(wallet)
|
|
require.Error(t, err)
|
|
|
|
// Mock the wallet to return empty utxos.
|
|
wallet.On("ListUnspentWitnessFromDefaultAccount",
|
|
min, max).Return([]*lnwallet.Utxo{}, nil).Once()
|
|
|
|
// Check that the error is returned from not having wallet inputs.
|
|
err = set.AddWalletInputs(wallet)
|
|
require.ErrorIs(t, err, ErrNotEnoughInputs)
|
|
}
|
|
|
|
// TestAddWalletInputNotEnoughInputs checks that when there are not enough
|
|
// wallet utxos, an error is returned and the budget set is reset to its
|
|
// initial state.
|
|
func TestAddWalletInputNotEnoughInputs(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
wallet := &MockWallet{}
|
|
defer wallet.AssertExpectations(t)
|
|
|
|
// Specify the min and max confs used in
|
|
// ListUnspentWitnessFromDefaultAccount.
|
|
min, max := int32(1), int32(math.MaxInt32)
|
|
|
|
// Assume the desired budget is 10k satoshis.
|
|
const budget = 10_000
|
|
|
|
// Create a mock input that has required outputs.
|
|
mockInput := &input.MockInput{}
|
|
mockInput.On("RequiredTxOut").Return(&wire.TxOut{})
|
|
defer mockInput.AssertExpectations(t)
|
|
|
|
// Create a pending input that requires 10k satoshis.
|
|
pi := &SweeperInput{
|
|
Input: mockInput,
|
|
params: Params{Budget: budget},
|
|
}
|
|
|
|
// Create a wallet utxo that cannot cover the budget.
|
|
utxo := &lnwallet.Utxo{
|
|
AddressType: lnwallet.WitnessPubKey,
|
|
Value: budget - 1,
|
|
}
|
|
|
|
// Mock the wallet to return the above utxo.
|
|
wallet.On("ListUnspentWitnessFromDefaultAccount",
|
|
min, max).Return([]*lnwallet.Utxo{utxo}, nil).Once()
|
|
|
|
// Initialize an input set with the pending input.
|
|
set := BudgetInputSet{inputs: []*SweeperInput{pi}}
|
|
|
|
// Add wallet inputs to the input set, which should give us an error as
|
|
// the wallet cannot cover the budget.
|
|
err := set.AddWalletInputs(wallet)
|
|
require.ErrorIs(t, err, ErrNotEnoughInputs)
|
|
|
|
// Check that the budget set is reverted to its initial state.
|
|
require.Len(t, set.inputs, 1)
|
|
require.Equal(t, pi, set.inputs[0])
|
|
}
|
|
|
|
// TestAddWalletInputSuccess checks that when there are enough wallet utxos,
|
|
// they are added to the input set.
|
|
func TestAddWalletInputSuccess(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
wallet := &MockWallet{}
|
|
defer wallet.AssertExpectations(t)
|
|
|
|
// Specify the min and max confs used in
|
|
// ListUnspentWitnessFromDefaultAccount.
|
|
min, max := int32(1), int32(math.MaxInt32)
|
|
|
|
// Assume the desired budget is 10k satoshis.
|
|
const budget = 10_000
|
|
|
|
// Create a mock input that has required outputs.
|
|
mockInput := &input.MockInput{}
|
|
mockInput.On("RequiredTxOut").Return(&wire.TxOut{})
|
|
defer mockInput.AssertExpectations(t)
|
|
|
|
// Create a pending input that requires 10k satoshis.
|
|
deadline := int32(1000)
|
|
pi := &SweeperInput{
|
|
Input: mockInput,
|
|
params: Params{
|
|
Budget: budget,
|
|
DeadlineHeight: fn.Some(deadline),
|
|
},
|
|
}
|
|
|
|
// Mock methods used in loggings.
|
|
//
|
|
// NOTE: these methods are not functional as they are only used for
|
|
// loggings in debug or trace mode so we use arbitrary values.
|
|
mockInput.On("OutPoint").Return(wire.OutPoint{Hash: chainhash.Hash{1}})
|
|
mockInput.On("WitnessType").Return(input.CommitmentAnchor)
|
|
|
|
// Create a wallet utxo that cannot cover the budget.
|
|
utxo := &lnwallet.Utxo{
|
|
AddressType: lnwallet.WitnessPubKey,
|
|
Value: budget - 1,
|
|
}
|
|
|
|
// Mock the wallet to return the two utxos which can cover the budget.
|
|
wallet.On("ListUnspentWitnessFromDefaultAccount",
|
|
min, max).Return([]*lnwallet.Utxo{utxo, utxo}, nil).Once()
|
|
|
|
// Initialize an input set with the pending input.
|
|
set, err := NewBudgetInputSet([]SweeperInput{*pi})
|
|
require.NoError(t, err)
|
|
|
|
// Add wallet inputs to the input set, which should give us an error as
|
|
// the wallet cannot cover the budget.
|
|
err = set.AddWalletInputs(wallet)
|
|
require.NoError(t, err)
|
|
|
|
// Check that the budget set is updated.
|
|
require.Len(t, set.inputs, 3)
|
|
|
|
// The first input is the pending input.
|
|
require.Equal(t, pi, set.inputs[0])
|
|
|
|
// The second and third inputs are wallet inputs that have
|
|
// DeadlineHeight set.
|
|
input2Deadline := set.inputs[1].params.DeadlineHeight
|
|
require.Equal(t, deadline, input2Deadline.UnsafeFromSome())
|
|
input3Deadline := set.inputs[2].params.DeadlineHeight
|
|
require.Equal(t, deadline, input3Deadline.UnsafeFromSome())
|
|
|
|
// Finally, check the interface methods.
|
|
require.EqualValues(t, budget, set.Budget())
|
|
require.Equal(t, deadline, set.DeadlineHeight().UnsafeFromSome())
|
|
// Weak check, a strong check is to open the slice and check each item.
|
|
require.Len(t, set.inputs, 3)
|
|
}
|