mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-14 12:43: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.
272 lines
6.5 KiB
Go
272 lines
6.5 KiB
Go
package test
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha256"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/btcsuite/btcd/wire/v2"
|
|
"github.com/lightninglabs/lndclient"
|
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
|
"github.com/lightningnetwork/lnd/lnrpc"
|
|
"github.com/lightningnetwork/lnd/lntypes"
|
|
"github.com/lightningnetwork/lnd/zpay32"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// Context contains shared test context functions.
|
|
type Context struct {
|
|
T *testing.T
|
|
Lnd *LndMockServices
|
|
FailedInvoices map[lntypes.Hash]struct{}
|
|
PaidInvoices map[string]func(error)
|
|
}
|
|
|
|
// NewContext instanties a new common test context.
|
|
func NewContext(t *testing.T,
|
|
lnd *LndMockServices) Context {
|
|
|
|
return Context{
|
|
T: t,
|
|
Lnd: lnd,
|
|
FailedInvoices: make(map[lntypes.Hash]struct{}),
|
|
PaidInvoices: make(map[string]func(error)),
|
|
}
|
|
}
|
|
|
|
// ReceiveTx receives and decodes a published tx.
|
|
func (ctx *Context) ReceiveTx() *wire.MsgTx {
|
|
ctx.T.Helper()
|
|
|
|
select {
|
|
case tx := <-ctx.Lnd.TxPublishChannel:
|
|
return tx
|
|
case <-time.After(Timeout):
|
|
ctx.T.Fatalf("sweep not published")
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// NotifySpend simulates a spend.
|
|
func (ctx *Context) NotifySpend(tx *wire.MsgTx, inputIndex uint32) {
|
|
ctx.T.Helper()
|
|
|
|
txHash := tx.TxHash()
|
|
|
|
select {
|
|
case ctx.Lnd.SpendChannel <- &chainntnfs.SpendDetail{
|
|
SpendingTx: tx,
|
|
SpenderTxHash: &txHash,
|
|
SpenderInputIndex: inputIndex,
|
|
}:
|
|
case <-time.After(Timeout):
|
|
ctx.T.Fatalf("spend not consumed")
|
|
}
|
|
}
|
|
|
|
// NotifyConf simulates a conf.
|
|
func (ctx *Context) NotifyConf(tx *wire.MsgTx) {
|
|
ctx.T.Helper()
|
|
|
|
select {
|
|
case ctx.Lnd.ConfChannel <- &chainntnfs.TxConfirmation{
|
|
Tx: tx,
|
|
}:
|
|
case <-time.After(Timeout):
|
|
ctx.T.Fatalf("confirmation not consumed")
|
|
}
|
|
}
|
|
|
|
// AssertRegisterSpendNtfn asserts that a register for spend has been received.
|
|
func (ctx *Context) AssertRegisterSpendNtfn(script []byte) {
|
|
ctx.T.Helper()
|
|
|
|
select {
|
|
case spendIntent := <-ctx.Lnd.RegisterSpendChannel:
|
|
require.Equal(
|
|
ctx.T, script, spendIntent.PkScript,
|
|
"server not listening for published script",
|
|
)
|
|
|
|
case <-time.After(Timeout):
|
|
DumpGoroutines()
|
|
ctx.T.Fatalf("spend not subscribed to")
|
|
}
|
|
}
|
|
|
|
// AssertTrackPayment asserts that a call was made to track payment, and
|
|
// returns the track payment message so that it can be used to send updates
|
|
// to the test.
|
|
func (ctx *Context) AssertTrackPayment() TrackPaymentMessage {
|
|
ctx.T.Helper()
|
|
|
|
var msg TrackPaymentMessage
|
|
select {
|
|
case msg = <-ctx.Lnd.TrackPaymentChannel:
|
|
|
|
case <-time.After(Timeout):
|
|
DumpGoroutines()
|
|
ctx.T.Fatalf("payment not tracked")
|
|
}
|
|
|
|
return msg
|
|
}
|
|
|
|
// AssertRegisterConf asserts that a register for conf has been received.
|
|
func (ctx *Context) AssertRegisterConf(expectTxHash bool, confs int32) *ConfRegistration {
|
|
ctx.T.Helper()
|
|
|
|
// Expect client to register for conf
|
|
var confIntent *ConfRegistration
|
|
select {
|
|
case confIntent = <-ctx.Lnd.RegisterConfChannel:
|
|
switch {
|
|
case expectTxHash && confIntent.TxID == nil:
|
|
ctx.T.Fatalf("expected tx id for registration")
|
|
|
|
case !expectTxHash && confIntent.TxID != nil:
|
|
ctx.T.Fatalf("expected script only registration")
|
|
}
|
|
|
|
// Require that we registered for the number of confirmations
|
|
// the test expects.
|
|
require.Equal(ctx.T, confs, confIntent.NumConfs)
|
|
|
|
case <-time.After(Timeout):
|
|
ctx.T.Fatalf("tx confirmed not subscribed to")
|
|
}
|
|
|
|
return confIntent
|
|
}
|
|
|
|
// AssertPaid asserts that the expected payment request has been paid. This
|
|
// function returns a complete function to signal the final payment result.
|
|
func (ctx *Context) AssertPaid(
|
|
expectedMemo string) func(error) {
|
|
|
|
ctx.T.Helper()
|
|
|
|
if done, ok := ctx.PaidInvoices[expectedMemo]; ok {
|
|
return done
|
|
}
|
|
|
|
// Assert that the client pays swap invoice.
|
|
for {
|
|
var swapPayment RouterPaymentChannelMessage
|
|
select {
|
|
case swapPayment = <-ctx.Lnd.RouterSendPaymentChannel:
|
|
case <-time.After(Timeout):
|
|
ctx.T.Fatalf("no payment sent for invoice: %v",
|
|
expectedMemo)
|
|
}
|
|
|
|
payReq := ctx.DecodeInvoice(swapPayment.SendPaymentRequest.Invoice)
|
|
|
|
_, ok := ctx.PaidInvoices[*payReq.Description]
|
|
require.False(
|
|
ctx.T, ok,
|
|
"duplicate invoice paid: %v", *payReq.Description,
|
|
)
|
|
|
|
done := func(result error) {
|
|
if result != nil {
|
|
// Send a terminal FAILED status so the client
|
|
// always receives a non-nil PaymentStatus and
|
|
// won't dereference nil.
|
|
swapPayment.Updates <- lndclient.PaymentStatus{
|
|
State: lnrpc.Payment_FAILED,
|
|
}
|
|
return
|
|
}
|
|
swapPayment.Updates <- lndclient.PaymentStatus{
|
|
State: lnrpc.Payment_SUCCEEDED,
|
|
}
|
|
}
|
|
|
|
ctx.PaidInvoices[*payReq.Description] = done
|
|
|
|
if *payReq.Description == expectedMemo {
|
|
return done
|
|
}
|
|
}
|
|
}
|
|
|
|
// AssertSettled asserts that an invoice with the given hash is settled.
|
|
func (ctx *Context) AssertSettled(
|
|
expectedHash lntypes.Hash) lntypes.Preimage {
|
|
|
|
ctx.T.Helper()
|
|
|
|
select {
|
|
case preimage := <-ctx.Lnd.SettleInvoiceChannel:
|
|
hash := sha256.Sum256(preimage[:])
|
|
require.Equal(
|
|
ctx.T, expectedHash, lntypes.Hash(hash),
|
|
"server claims with wrong preimage",
|
|
)
|
|
|
|
return preimage
|
|
case <-time.After(Timeout):
|
|
}
|
|
ctx.T.Fatalf("invoice not settled")
|
|
return lntypes.Preimage{}
|
|
}
|
|
|
|
// AssertFailed asserts that an invoice with the given hash is failed.
|
|
func (ctx *Context) AssertFailed(expectedHash lntypes.Hash) {
|
|
ctx.T.Helper()
|
|
|
|
if _, ok := ctx.FailedInvoices[expectedHash]; ok {
|
|
return
|
|
}
|
|
|
|
for {
|
|
select {
|
|
case hash := <-ctx.Lnd.FailInvoiceChannel:
|
|
ctx.FailedInvoices[expectedHash] = struct{}{}
|
|
if expectedHash == hash {
|
|
return
|
|
}
|
|
case <-time.After(Timeout):
|
|
ctx.T.Fatalf("invoice not failed")
|
|
}
|
|
}
|
|
}
|
|
|
|
// DecodeInvoice decodes a payment request string.
|
|
func (ctx *Context) DecodeInvoice(request string) *zpay32.Invoice {
|
|
ctx.T.Helper()
|
|
|
|
payReq, err := ctx.Lnd.DecodeInvoice(request)
|
|
require.NoError(ctx.T, err)
|
|
|
|
return payReq
|
|
}
|
|
|
|
// GetOutputIndex returns the index in the tx outs of the given script hash.
|
|
func (ctx *Context) GetOutputIndex(tx *wire.MsgTx,
|
|
script []byte) int {
|
|
|
|
for idx, out := range tx.TxOut {
|
|
if bytes.Equal(out.PkScript, script) {
|
|
return idx
|
|
}
|
|
}
|
|
|
|
ctx.T.Fatal("the output not present in tx")
|
|
return 0
|
|
}
|
|
|
|
// NotifyServerHeight notifies the server of the arrival of a new block and
|
|
// waits for the notification to be processed by selecting on a
|
|
// dedicated test channel.
|
|
func (ctx *Context) NotifyServerHeight(height int32) {
|
|
require.NoError(ctx.T, ctx.Lnd.NotifyHeight(height))
|
|
}
|
|
|
|
func (ctx *Context) AssertEpochListeners(numListeners int32) {
|
|
require.Eventually(ctx.T, func() bool {
|
|
return ctx.Lnd.EpochSubscribers() == numListeners
|
|
}, Timeout, time.Millisecond*250)
|
|
}
|