loop/staticaddr/loopin/loopin_test.go
Boris Nagaev d554b42ef6
multi: migrate to btcd v2 modules
Update LND, Aperture, and Taproot Assets to revisions using the
btcd v2 modules, and update lndclient to v0.21.0-3. Migrate Loop
chain, transaction, and address types to their corresponding v2
packages.

The lndclient release includes the migration from:
https://github.com/lightninglabs/lndclient/pull/280

Taproot Assets is temporarily replaced with its btcd v2 revision
because the v0.8 release branch has not adopted the new modules.

This raises the minimum Go version to 1.26 and changes exported
address types.
2026-08-12 23:39:26 +00:00

194 lines
5.4 KiB
Go

package loopin
import (
"bytes"
"context"
"testing"
"time"
btcaddr "github.com/btcsuite/btcd/address/v2"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil/v2"
"github.com/btcsuite/btcd/chaincfg/v2"
"github.com/btcsuite/btcd/chainhash/v2"
"github.com/btcsuite/btcd/wire/v2"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop/staticaddr/deposit"
"github.com/lightninglabs/loop/staticaddr/script"
"github.com/lightninglabs/loop/staticaddr/version"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/stretchr/testify/require"
)
// noopSigner is a minimal SignerClient mock that returns dummy signatures
// without blocking on channels.
type noopSigner struct {
lndclient.SignerClient
}
// SignOutputRaw returns dummy 64-byte signatures for each sign descriptor.
func (s *noopSigner) SignOutputRaw(_ context.Context, _ *wire.MsgTx,
descs []*lndclient.SignDescriptor, _ []*wire.TxOut) ([][]byte, error) {
sigs := make([][]byte, len(descs))
for i := range descs {
sigs[i] = make([]byte, 64)
}
return sigs, nil
}
// TestCreateHtlcSweepTxSweepValue verifies that createHtlcSweepTx derives the
// sweep output value from the HTLC output, not the change output. When a change
// output is present, the sweep must reference the HTLC output value.
func TestCreateHtlcSweepTxSweepValue(t *testing.T) {
t.Parallel()
clientKey, err := btcec.NewPrivateKey()
require.NoError(t, err)
serverKey, err := btcec.NewPrivateKey()
require.NoError(t, err)
network := &chaincfg.RegressionNetParams
swapHash := lntypes.Hash{1, 2, 3}
// Create a static address to derive PkScript.
staticAddr, err := newStaticAddress(
clientKey.PubKey(), serverKey.PubKey(), 4032,
)
require.NoError(t, err)
pkScript, err := staticAddr.StaticAddressScript()
require.NoError(t, err)
addrParams := &script.Parameters{
ClientPubkey: clientKey.PubKey(),
ServerPubkey: serverKey.PubKey(),
PkScript: pkScript,
Expiry: 4032,
ProtocolVersion: version.ProtocolVersion_V0,
}
depositValue := btcutil.Amount(500_000)
deposits := []*deposit.Deposit{
{
OutPoint: wire.OutPoint{
Hash: chainhash.Hash{0xaa},
Index: 0,
},
Value: depositValue,
},
}
feeRate := chainfee.SatPerKWeight(253)
maxFeePercentage := 0.2
// SelectedAmount < total triggers a change output.
selectedAmount := btcutil.Amount(300_000)
loopIn := &StaticAddressLoopIn{
SwapHash: swapHash,
HtlcCltvExpiry: 800,
InitiationHeight: 100,
InitiationTime: time.Now(),
ProtocolVersion: version.ProtocolVersion_V0,
ClientPubkey: clientKey.PubKey(),
ServerPubkey: serverKey.PubKey(),
Deposits: deposits,
AddressParams: addrParams,
HtlcTxFeeRate: feeRate,
SelectedAmount: selectedAmount,
PaymentTimeoutSeconds: 3600,
}
sweepAddr, err := btcaddr.NewAddressTaproot(
make([]byte, 32), network,
)
require.NoError(t, err)
signer := &noopSigner{}
// Build the HTLC transaction once. It has two outputs with distinct
// values: the HTLC output and a change output.
htlcTx, err := loopIn.createHtlcTx(network, feeRate, maxFeePercentage)
require.NoError(t, err)
require.Len(t, htlcTx.TxOut, 2, "expected HTLC + change outputs")
// Identify which output is change and which is HTLC.
var htlcIdx int
if bytes.Equal(htlcTx.TxOut[0].PkScript, pkScript) {
htlcIdx = 1
}
htlcValue := htlcTx.TxOut[htlcIdx].Value
changeValue := htlcTx.TxOut[1-htlcIdx].Value
require.NotEqual(t, htlcValue, changeValue,
"HTLC and change values must differ for this test to be "+
"meaningful")
// Call createHtlcSweepTx and verify that the sweep output is derived
// from the HTLC value, not the change.
sweepTx, err := loopIn.createHtlcSweepTx(
t.Context(), signer, sweepAddr, feeRate,
network, uint32(loopIn.HtlcCltvExpiry)+1,
maxFeePercentage,
)
require.NoError(t, err)
require.Len(t, sweepTx.TxOut, 1)
sweepValue := sweepTx.TxOut[0].Value
require.Greater(t, sweepValue, int64(0))
require.LessOrEqual(t, sweepValue, htlcValue,
"sweep value must not exceed HTLC output value")
require.Greater(t, sweepValue, changeValue,
"sweep value should be greater than change "+
"value, confirming it was derived from "+
"the HTLC output")
}
// TestPaymentTimeoutDuration verifies that zero timeout values fall back to the
// default payment timeout duration.
func TestPaymentTimeoutDuration(t *testing.T) {
t.Parallel()
tests := []struct {
name string
paymentTimeoutSeconds uint32
expected time.Duration
}{
{
name: "default",
expected: time.Duration(DefaultPaymentTimeoutSeconds) * time.Second,
},
{
name: "configured",
paymentTimeoutSeconds: 42,
expected: 42 * time.Second,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
t.Parallel()
loopIn := &StaticAddressLoopIn{
PaymentTimeoutSeconds: test.paymentTimeoutSeconds,
}
require.Equal(
t, test.expected, loopIn.PaymentTimeoutDuration(),
)
})
}
}
// newStaticAddress creates a StaticAddress for testing.
func newStaticAddress(clientKey, serverKey *btcec.PublicKey,
csvExpiry int64) (*script.StaticAddress, error) {
return script.NewStaticAddress(
input.MuSig2Version100RC2, csvExpiry, clientKey, serverKey,
)
}