mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-13 12:33:03 +02:00
Include https://github.com/lightninglabs/lndclient/pull/280 multi: migrate to btcd v2 modules + add WalletKit.SubmitPackage Migrate Loop imports and update Aperture and Taproot Assets to compatible revisions so this commit remains green on its own.
308 lines
8.1 KiB
Go
308 lines
8.1 KiB
Go
package loop
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
|
|
"github.com/btcsuite/btcd/btcutil/v2"
|
|
"github.com/btcsuite/btcd/chaincfg/v2"
|
|
"github.com/btcsuite/btcd/wire/v2"
|
|
"github.com/lightninglabs/lndclient"
|
|
"github.com/lightninglabs/loop/loopdb"
|
|
"github.com/lightninglabs/loop/test"
|
|
"github.com/lightningnetwork/lnd/input"
|
|
invpkg "github.com/lightningnetwork/lnd/invoices"
|
|
"github.com/lightningnetwork/lnd/lntypes"
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
|
"github.com/lightningnetwork/lnd/routing/route"
|
|
"github.com/lightningnetwork/lnd/zpay32"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
var (
|
|
testTime = time.Date(2018, time.January, 9, 14, 00, 00, 0, time.UTC)
|
|
|
|
testLoopOutMinOnChainCltvDelta = int32(30)
|
|
testLoopOutMaxOnChainCltvDelta = int32(40)
|
|
testChargeOnChainCltvDelta = int32(100)
|
|
testSwapFee = btcutil.Amount(210)
|
|
testFixedPrepayAmount = btcutil.Amount(100)
|
|
testMinSwapAmount = btcutil.Amount(10000)
|
|
testMaxSwapAmount = btcutil.Amount(1000000)
|
|
)
|
|
|
|
// mockMuSig2SigningData returns size-correct placeholder data. Client tests
|
|
// only assert response handling, not MuSig2 cryptographic validity.
|
|
func mockMuSig2SigningData() ([]byte, []byte, error) {
|
|
return make([]byte, musig2.PubNonceSize),
|
|
make([]byte, input.MuSig2PartialSigSize), nil
|
|
}
|
|
|
|
// serverMock is used in client unit tests to simulate swap server behaviour.
|
|
type serverMock struct {
|
|
expectedSwapAmt btcutil.Amount
|
|
swapInvoiceAmt btcutil.Amount
|
|
prepayInvoiceAmt btcutil.Amount
|
|
|
|
height int32
|
|
|
|
swapInvoice string
|
|
probeInvoice string
|
|
swapHash lntypes.Hash
|
|
prepayHash lntypes.Hash
|
|
|
|
// preimagePush is a channel that preimage pushes are sent into.
|
|
preimagePush chan lntypes.Preimage
|
|
|
|
// cancelSwap is a channel that swap cancellations are sent into.
|
|
cancelSwap chan *outCancelDetails
|
|
|
|
lnd *test.LndMockServices
|
|
}
|
|
|
|
var _ swapServerClient = (*serverMock)(nil)
|
|
|
|
func newServerMock(lnd *test.LndMockServices) *serverMock {
|
|
return &serverMock{
|
|
expectedSwapAmt: 50000,
|
|
|
|
// Total swap fee: 1000 + 0.01 * 50000 = 1050
|
|
swapInvoiceAmt: 50950,
|
|
prepayInvoiceAmt: 100,
|
|
|
|
height: 600,
|
|
|
|
preimagePush: make(chan lntypes.Preimage),
|
|
cancelSwap: make(chan *outCancelDetails),
|
|
|
|
lnd: lnd,
|
|
}
|
|
}
|
|
|
|
func (s *serverMock) NewLoopOutSwap(_ context.Context, swapHash lntypes.Hash,
|
|
amount btcutil.Amount, _ int32, _ [33]byte, _ time.Time,
|
|
_ string) (*newLoopOutResponse, error) {
|
|
|
|
_, senderKey := test.CreateKey(100)
|
|
|
|
if amount != s.expectedSwapAmt {
|
|
return nil, errors.New("unexpected test swap amount")
|
|
}
|
|
|
|
s.swapHash = swapHash
|
|
swapPayReqString, err := getInvoice(swapHash, s.swapInvoiceAmt,
|
|
swapInvoiceDesc)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Set the prepay hash to be different from the swap hash.
|
|
s.prepayHash = swapHash
|
|
s.prepayHash[0] ^= 1
|
|
prePayReqString, err := getInvoice(s.prepayHash, s.prepayInvoiceAmt,
|
|
prepayInvoiceDesc)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var senderKeyArray [33]byte
|
|
copy(senderKeyArray[:], senderKey.SerializeCompressed())
|
|
|
|
return &newLoopOutResponse{
|
|
senderKey: senderKeyArray,
|
|
swapInvoice: swapPayReqString,
|
|
prepayInvoice: prePayReqString,
|
|
}, nil
|
|
}
|
|
|
|
func (s *serverMock) GetLoopOutTerms(ctx context.Context, initiator string) (
|
|
*LoopOutTerms, error) {
|
|
|
|
return &LoopOutTerms{
|
|
MinSwapAmount: testMinSwapAmount,
|
|
MaxSwapAmount: testMaxSwapAmount,
|
|
MinCltvDelta: testLoopOutMinOnChainCltvDelta,
|
|
MaxCltvDelta: testLoopOutMaxOnChainCltvDelta,
|
|
}, nil
|
|
}
|
|
|
|
func (s *serverMock) GetLoopOutQuote(ctx context.Context, amt btcutil.Amount,
|
|
expiry int32, _ time.Time, initiator string) (*LoopOutQuote, error) {
|
|
|
|
dest := [33]byte{1, 2, 3}
|
|
|
|
return &LoopOutQuote{
|
|
SwapFee: testSwapFee,
|
|
SwapPaymentDest: dest,
|
|
PrepayAmount: testFixedPrepayAmount,
|
|
}, nil
|
|
}
|
|
|
|
func getInvoice(hash lntypes.Hash, amt btcutil.Amount, memo string) (string, error) {
|
|
// Set different payment addresses for swap invoices.
|
|
payAddr := [32]byte{1, 2, 3}
|
|
if memo == swapInvoiceDesc {
|
|
payAddr = [32]byte{3, 2, 1}
|
|
}
|
|
|
|
req, err := zpay32.NewInvoice(
|
|
&chaincfg.TestNet3Params, hash, testTime,
|
|
zpay32.Description(memo),
|
|
zpay32.Amount(lnwire.MilliSatoshi(1000*amt)),
|
|
zpay32.PaymentAddr(payAddr),
|
|
)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
reqString, err := test.EncodePayReq(req)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return reqString, nil
|
|
}
|
|
|
|
func (s *serverMock) NewLoopInSwap(_ context.Context, swapHash lntypes.Hash,
|
|
amount btcutil.Amount, _, _ [33]byte, swapInvoice, probeInvoice string,
|
|
_ *route.Vertex, _ string) (*newLoopInResponse, error) {
|
|
|
|
_, receiverKey := test.CreateKey(101)
|
|
_, receiverInternalKey := test.CreateKey(102)
|
|
|
|
if amount != s.expectedSwapAmt {
|
|
return nil, errors.New("unexpected test swap amount")
|
|
}
|
|
|
|
var receiverKeyArray, receiverInternalKeyArray [33]byte
|
|
copy(receiverKeyArray[:], receiverKey.SerializeCompressed())
|
|
copy(
|
|
receiverInternalKeyArray[:],
|
|
receiverInternalKey.SerializeCompressed(),
|
|
)
|
|
|
|
s.swapInvoice = swapInvoice
|
|
s.probeInvoice = probeInvoice
|
|
s.swapHash = swapHash
|
|
|
|
// Simulate the server paying the probe invoice and expect the client to
|
|
// cancel the probe payment.
|
|
probeSub := <-s.lnd.SingleInvoiceSubcribeChannel
|
|
probeSub.Update <- lndclient.InvoiceUpdate{
|
|
Invoice: lndclient.Invoice{
|
|
State: invpkg.ContractAccepted,
|
|
},
|
|
}
|
|
<-s.lnd.FailInvoiceChannel
|
|
|
|
resp := &newLoopInResponse{
|
|
expiry: s.height + testChargeOnChainCltvDelta,
|
|
receiverKey: receiverKeyArray,
|
|
receiverInternalKey: receiverInternalKeyArray,
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
func (s *serverMock) PushLoopOutPreimage(_ context.Context,
|
|
preimage lntypes.Preimage) error {
|
|
|
|
// Push the preimage into the mock's preimage channel.
|
|
s.preimagePush <- preimage
|
|
|
|
return nil
|
|
}
|
|
|
|
// CancelLoopOutSwap pushes a request to cancel a swap into our mock's channel.
|
|
func (s *serverMock) CancelLoopOutSwap(ctx context.Context,
|
|
details *outCancelDetails) error {
|
|
|
|
s.cancelSwap <- details
|
|
return nil
|
|
}
|
|
|
|
func (s *serverMock) assertSwapCanceled(t *testing.T, details *outCancelDetails) {
|
|
require.Equal(t, details, <-s.cancelSwap)
|
|
}
|
|
|
|
func (s *serverMock) GetLoopInTerms(ctx context.Context, initiator string) (
|
|
*LoopInTerms, error) {
|
|
|
|
return &LoopInTerms{
|
|
MinSwapAmount: testMinSwapAmount,
|
|
MaxSwapAmount: testMaxSwapAmount,
|
|
}, nil
|
|
}
|
|
|
|
func (s *serverMock) GetLoopInQuote(context.Context, btcutil.Amount,
|
|
route.Vertex, *route.Vertex, [][]zpay32.HopHint, string,
|
|
uint32, bool) (*LoopInQuote, error) {
|
|
|
|
return &LoopInQuote{
|
|
SwapFee: testSwapFee,
|
|
CltvDelta: testChargeOnChainCltvDelta,
|
|
}, nil
|
|
}
|
|
|
|
// SubscribeLoopOutUpdates provides a mocked implementation of state
|
|
// subscriptions.
|
|
func (s *serverMock) SubscribeLoopOutUpdates(_ context.Context,
|
|
_ lntypes.Hash) (<-chan *ServerUpdate, <-chan error, error) {
|
|
|
|
return nil, nil, nil
|
|
}
|
|
|
|
// SubscribeLoopInUpdates provides a mocked implementation of state subscriptions.
|
|
func (s *serverMock) SubscribeLoopInUpdates(_ context.Context,
|
|
_ lntypes.Hash) (<-chan *ServerUpdate, <-chan error, error) {
|
|
|
|
return nil, nil, nil
|
|
}
|
|
|
|
func (s *serverMock) Probe(ctx context.Context, amt btcutil.Amount,
|
|
pubKey route.Vertex, lastHop *route.Vertex,
|
|
routeHints [][]zpay32.HopHint) error {
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *serverMock) RecommendRoutingPlugin(_ context.Context, _ lntypes.Hash,
|
|
_ [32]byte) (RoutingPluginType, error) {
|
|
|
|
return RoutingPluginNone, nil
|
|
}
|
|
|
|
func (s *serverMock) ReportRoutingResult(_ context.Context, _ lntypes.Hash,
|
|
_ [32]byte, _ RoutingPluginType, _ bool, _ int32, _ int64) error {
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *serverMock) MuSig2SignSweep(_ context.Context, _ loopdb.ProtocolVersion,
|
|
_ lntypes.Hash, _ [32]byte, _ []byte, _ []byte) ([]byte,
|
|
[]byte, error) {
|
|
|
|
return mockMuSig2SigningData()
|
|
}
|
|
|
|
func (s *serverMock) MultiMuSig2SignSweep(ctx context.Context,
|
|
protocolVersion loopdb.ProtocolVersion, swapHash lntypes.Hash,
|
|
paymentAddr [32]byte, nonce []byte, sweepTxPsbt []byte,
|
|
prevoutMap map[wire.OutPoint]*wire.TxOut) (
|
|
[]byte, []byte, error) {
|
|
|
|
return mockMuSig2SigningData()
|
|
}
|
|
|
|
func (s *serverMock) PushKey(_ context.Context, _ loopdb.ProtocolVersion,
|
|
_ lntypes.Hash, _ [32]byte) error {
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *serverMock) FetchL402(_ context.Context) error {
|
|
return nil
|
|
}
|