mirror of
https://github.com/aljazceru/lspd.git
synced 2026-02-23 15:04:23 +01:00
notifications: add integration tests
This commit is contained in:
2
go.mod
2
go.mod
@@ -4,7 +4,7 @@ go 1.19
|
||||
|
||||
require (
|
||||
github.com/aws/aws-sdk-go v1.34.0
|
||||
github.com/breez/lntest v0.0.21
|
||||
github.com/breez/lntest v0.0.23
|
||||
github.com/btcsuite/btcd v0.23.5-0.20230228185050-38331963bddd
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.2
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2
|
||||
|
||||
@@ -3,6 +3,7 @@ package itest
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"log"
|
||||
"testing"
|
||||
|
||||
"github.com/breez/lntest"
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
@@ -19,6 +20,7 @@ type BreezClient interface {
|
||||
Start()
|
||||
Stop() error
|
||||
SetHtlcAcceptor(totalMsat uint64)
|
||||
ResetHtlcAcceptor()
|
||||
}
|
||||
|
||||
type generateInvoicesRequest struct {
|
||||
@@ -39,24 +41,55 @@ func GenerateInvoices(n BreezClient, req generateInvoicesRequest) (invoice, invo
|
||||
preimage, err := GenerateRandomBytes(32)
|
||||
lntest.CheckError(n.Harness().T, err)
|
||||
|
||||
lspNodeId, err := btcec.ParsePubKey(req.lsp.NodeId())
|
||||
lntest.CheckError(n.Harness().T, err)
|
||||
|
||||
innerInvoice := n.Node().CreateBolt11Invoice(&lntest.CreateInvoiceOptions{
|
||||
AmountMsat: req.innerAmountMsat,
|
||||
Description: &req.description,
|
||||
Preimage: &preimage,
|
||||
})
|
||||
outerInvoiceRaw, err := zpay32.Decode(innerInvoice.Bolt11, &chaincfg.RegressionNetParams)
|
||||
outerInvoice := AddHopHint(n, innerInvoice.Bolt11, req.lsp, lntest.ShortChannelID{
|
||||
BlockHeight: 1,
|
||||
TxIndex: 0,
|
||||
OutputIndex: 0,
|
||||
}, &req.outerAmountMsat)
|
||||
|
||||
inner := invoice{
|
||||
bolt11: innerInvoice.Bolt11,
|
||||
paymentHash: innerInvoice.PaymentHash,
|
||||
paymentSecret: innerInvoice.PaymentSecret,
|
||||
paymentPreimage: preimage,
|
||||
}
|
||||
outer := invoice{
|
||||
bolt11: outerInvoice,
|
||||
paymentHash: innerInvoice.PaymentHash[:],
|
||||
paymentSecret: innerInvoice.PaymentSecret,
|
||||
paymentPreimage: preimage,
|
||||
}
|
||||
|
||||
return inner, outer
|
||||
}
|
||||
|
||||
func ContainsHopHint(t *testing.T, invoice string) bool {
|
||||
rawInvoice, err := zpay32.Decode(invoice, &chaincfg.RegressionNetParams)
|
||||
lntest.CheckError(t, err)
|
||||
|
||||
return len(rawInvoice.RouteHints) > 0
|
||||
}
|
||||
|
||||
func AddHopHint(n BreezClient, invoice string, lsp LspNode, chanid lntest.ShortChannelID, amountMsat *uint64) string {
|
||||
rawInvoice, err := zpay32.Decode(invoice, &chaincfg.RegressionNetParams)
|
||||
lntest.CheckError(n.Harness().T, err)
|
||||
|
||||
milliSat := lnwire.MilliSatoshi(req.outerAmountMsat)
|
||||
outerInvoiceRaw.MilliSat = &milliSat
|
||||
fakeChanId := &lnwire.ShortChannelID{BlockHeight: 1, TxIndex: 0, TxPosition: 0}
|
||||
outerInvoiceRaw.RouteHints = append(outerInvoiceRaw.RouteHints, []zpay32.HopHint{
|
||||
if amountMsat != nil {
|
||||
milliSat := lnwire.MilliSatoshi(*amountMsat)
|
||||
rawInvoice.MilliSat = &milliSat
|
||||
}
|
||||
|
||||
lspNodeId, err := btcec.ParsePubKey(lsp.NodeId())
|
||||
lntest.CheckError(n.Harness().T, err)
|
||||
rawInvoice.RouteHints = append(rawInvoice.RouteHints, []zpay32.HopHint{
|
||||
{
|
||||
NodeID: lspNodeId,
|
||||
ChannelID: fakeChanId.ToUint64(),
|
||||
ChannelID: chanid.ToUint64(),
|
||||
FeeBaseMSat: lspBaseFeeMsat,
|
||||
FeeProportionalMillionths: lspFeeRatePpm,
|
||||
CLTVExpiryDelta: lspCltvDelta,
|
||||
@@ -64,12 +97,12 @@ func GenerateInvoices(n BreezClient, req generateInvoicesRequest) (invoice, invo
|
||||
})
|
||||
|
||||
log.Printf(
|
||||
"Encoding outer invoice. privkey: '%x', invoice: '%+v', original bolt11: '%s'",
|
||||
"Encoding invoice. privkey: '%x', invoice: '%+v', original bolt11: '%s'",
|
||||
n.Node().PrivateKey().Serialize(),
|
||||
outerInvoiceRaw,
|
||||
innerInvoice.Bolt11,
|
||||
rawInvoice,
|
||||
invoice,
|
||||
)
|
||||
outerInvoice, err := outerInvoiceRaw.Encode(zpay32.MessageSigner{
|
||||
newInvoice, err := rawInvoice.Encode(zpay32.MessageSigner{
|
||||
SignCompact: func(msg []byte) ([]byte, error) {
|
||||
hash := sha256.Sum256(msg)
|
||||
sig, err := ecdsa.SignCompact(n.Node().PrivateKey(), hash[:], true)
|
||||
@@ -85,18 +118,5 @@ func GenerateInvoices(n BreezClient, req generateInvoicesRequest) (invoice, invo
|
||||
})
|
||||
lntest.CheckError(n.Harness().T, err)
|
||||
|
||||
inner := invoice{
|
||||
bolt11: innerInvoice.Bolt11,
|
||||
paymentHash: innerInvoice.PaymentHash,
|
||||
paymentSecret: innerInvoice.PaymentSecret,
|
||||
paymentPreimage: preimage,
|
||||
}
|
||||
outer := invoice{
|
||||
bolt11: outerInvoice,
|
||||
paymentHash: outerInvoiceRaw.PaymentHash[:],
|
||||
paymentSecret: innerInvoice.PaymentSecret,
|
||||
paymentPreimage: preimage,
|
||||
}
|
||||
|
||||
return inner, outer
|
||||
return newInvoice
|
||||
}
|
||||
|
||||
@@ -123,6 +123,9 @@ func (c *clnBreezClient) Start() {
|
||||
c.startHtlcAcceptor()
|
||||
}
|
||||
|
||||
func (c *clnBreezClient) ResetHtlcAcceptor() {
|
||||
c.htlcAcceptor = nil
|
||||
}
|
||||
func (c *clnBreezClient) SetHtlcAcceptor(totalMsat uint64) {
|
||||
c.htlcAcceptor = func(htlc *proto.HtlcAccepted) *proto.HtlcResolution {
|
||||
origPayload, err := hex.DecodeString(htlc.Onion.Payload)
|
||||
@@ -230,13 +233,12 @@ func (c *clnBreezClient) startHtlcAcceptor() {
|
||||
}
|
||||
|
||||
client := proto.NewClnPluginClient(conn)
|
||||
acceptor, err := client.HtlcStream(ctx)
|
||||
if err != nil {
|
||||
log.Printf("%s: client.HtlcStream() error: %v", c.name, err)
|
||||
break
|
||||
}
|
||||
for {
|
||||
acceptor, err := client.HtlcStream(ctx)
|
||||
if err != nil {
|
||||
log.Printf("%s: client.HtlcStream() error: %v", c.name, err)
|
||||
break
|
||||
}
|
||||
|
||||
htlc, err := acceptor.Recv()
|
||||
if err != nil {
|
||||
log.Printf("%s: acceptor.Recv() error: %v", c.name, err)
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/breez/lntest"
|
||||
"github.com/breez/lspd/config"
|
||||
"github.com/breez/lspd/notifications"
|
||||
lspd "github.com/breez/lspd/rpc"
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
ecies "github.com/ecies/go/v2"
|
||||
@@ -36,10 +37,11 @@ type ClnLspNode struct {
|
||||
}
|
||||
|
||||
type clnLspNodeRuntime struct {
|
||||
logFile *os.File
|
||||
cmd *exec.Cmd
|
||||
rpc lspd.ChannelOpenerClient
|
||||
cleanups []*lntest.Cleanup
|
||||
logFile *os.File
|
||||
cmd *exec.Cmd
|
||||
rpc lspd.ChannelOpenerClient
|
||||
notificationRpc notifications.NotificationsClient
|
||||
cleanups []*lntest.Cleanup
|
||||
}
|
||||
|
||||
func NewClnLspdNode(h *lntest.TestHarness, m *lntest.Miner, mem *mempoolApi, name string, nodeConfig *config.NodeConfig) LspNode {
|
||||
@@ -170,11 +172,13 @@ func (c *ClnLspNode) Start() {
|
||||
})
|
||||
|
||||
client := lspd.NewChannelOpenerClient(conn)
|
||||
notif := notifications.NewNotificationsClient(conn)
|
||||
c.runtime = &clnLspNodeRuntime{
|
||||
logFile: logFile,
|
||||
cmd: cmd,
|
||||
rpc: client,
|
||||
cleanups: cleanups,
|
||||
logFile: logFile,
|
||||
cmd: cmd,
|
||||
rpc: client,
|
||||
notificationRpc: notif,
|
||||
cleanups: cleanups,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,6 +211,10 @@ func (c *ClnLspNode) Rpc() lspd.ChannelOpenerClient {
|
||||
return c.runtime.rpc
|
||||
}
|
||||
|
||||
func (c *ClnLspNode) NotificationsRpc() notifications.NotificationsClient {
|
||||
return c.runtime.notificationRpc
|
||||
}
|
||||
|
||||
func (l *ClnLspNode) NodeId() []byte {
|
||||
return l.lightningNode.NodeId()
|
||||
}
|
||||
|
||||
@@ -78,6 +78,10 @@ func (c *lndBreezClient) Stop() error {
|
||||
return c.node.Stop()
|
||||
}
|
||||
|
||||
func (c *lndBreezClient) ResetHtlcAcceptor() {
|
||||
|
||||
}
|
||||
|
||||
func (c *lndBreezClient) SetHtlcAcceptor(totalMsat uint64) {
|
||||
// No need for a htlc acceptor in the LND breez client
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
|
||||
"github.com/breez/lntest"
|
||||
"github.com/breez/lspd/config"
|
||||
"github.com/breez/lspd/notifications"
|
||||
lspd "github.com/breez/lspd/rpc"
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
ecies "github.com/ecies/go/v2"
|
||||
@@ -32,10 +33,11 @@ type LndLspNode struct {
|
||||
}
|
||||
|
||||
type lndLspNodeRuntime struct {
|
||||
logFile *os.File
|
||||
cmd *exec.Cmd
|
||||
rpc lspd.ChannelOpenerClient
|
||||
cleanups []*lntest.Cleanup
|
||||
logFile *os.File
|
||||
cmd *exec.Cmd
|
||||
rpc lspd.ChannelOpenerClient
|
||||
notificationRpc notifications.NotificationsClient
|
||||
cleanups []*lntest.Cleanup
|
||||
}
|
||||
|
||||
func NewLndLspdNode(h *lntest.TestHarness, m *lntest.Miner, mem *mempoolApi, name string, nodeConfig *config.NodeConfig) LspNode {
|
||||
@@ -193,11 +195,13 @@ func (c *LndLspNode) Start() {
|
||||
})
|
||||
|
||||
client := lspd.NewChannelOpenerClient(conn)
|
||||
notifyClient := notifications.NewNotificationsClient(conn)
|
||||
c.runtime = &lndLspNodeRuntime{
|
||||
logFile: logFile,
|
||||
cmd: cmd,
|
||||
rpc: client,
|
||||
cleanups: cleanups,
|
||||
logFile: logFile,
|
||||
cmd: cmd,
|
||||
rpc: client,
|
||||
notificationRpc: notifyClient,
|
||||
cleanups: cleanups,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,6 +234,10 @@ func (c *LndLspNode) Rpc() lspd.ChannelOpenerClient {
|
||||
return c.runtime.rpc
|
||||
}
|
||||
|
||||
func (c *LndLspNode) NotificationsRpc() notifications.NotificationsClient {
|
||||
return c.runtime.notificationRpc
|
||||
}
|
||||
|
||||
func (l *LndLspNode) NodeId() []byte {
|
||||
return l.lightningNode.NodeId()
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
|
||||
"github.com/breez/lntest"
|
||||
"github.com/breez/lspd/config"
|
||||
"github.com/breez/lspd/notifications"
|
||||
lspd "github.com/breez/lspd/rpc"
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
@@ -45,6 +46,7 @@ type LspNode interface {
|
||||
PublicKey() *btcec.PublicKey
|
||||
EciesPublicKey() *ecies.PublicKey
|
||||
Rpc() lspd.ChannelOpenerClient
|
||||
NotificationsRpc() notifications.NotificationsClient
|
||||
NodeId() []byte
|
||||
LightningNode() lntest.LightningNode
|
||||
PostgresBackend() *PostgresContainer
|
||||
|
||||
@@ -151,4 +151,16 @@ var allTestCases = []*testCase{
|
||||
name: "testDynamicFeeFlow",
|
||||
test: testDynamicFeeFlow,
|
||||
},
|
||||
{
|
||||
name: "testOfflineNotificationPaymentRegistered",
|
||||
test: testOfflineNotificationPaymentRegistered,
|
||||
},
|
||||
{
|
||||
name: "testOfflineNotificationRegularForward",
|
||||
test: testOfflineNotificationRegularForward,
|
||||
},
|
||||
{
|
||||
name: "testOfflineNotificationZeroConfChannel",
|
||||
test: testOfflineNotificationZeroConfChannel,
|
||||
},
|
||||
}
|
||||
|
||||
59
itest/notification_service.go
Normal file
59
itest/notification_service.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package itest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type PaymentReceivedPayload struct {
|
||||
Template string `json:"template" binding:"required,eq=payment_received"`
|
||||
Data struct {
|
||||
PaymentHash string `json:"payment_hash" binding:"required"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type TxConfirmedPayload struct {
|
||||
Template string `json:"template" binding:"required,eq=tx_confirmed"`
|
||||
Data struct {
|
||||
TxID string `json:"tx_id" binding:"required"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type AddressTxsChangedPayload struct {
|
||||
Template string `json:"template" binding:"required,eq=address_txs_changed"`
|
||||
Data struct {
|
||||
Address string `json:"address" binding:"required"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type notificationDeliveryService struct {
|
||||
addr string
|
||||
handleFunc func(resp http.ResponseWriter, req *http.Request)
|
||||
}
|
||||
|
||||
func newNotificationDeliveryService(
|
||||
addr string,
|
||||
handleFunc func(resp http.ResponseWriter, req *http.Request),
|
||||
) *notificationDeliveryService {
|
||||
return ¬ificationDeliveryService{
|
||||
addr: addr,
|
||||
handleFunc: handleFunc,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *notificationDeliveryService) Start(ctx context.Context) error {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/api/v1/notify", s.handleFunc)
|
||||
lis, err := net.Listen("tcp", s.addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
lis.Close()
|
||||
}()
|
||||
|
||||
return http.Serve(lis, mux)
|
||||
}
|
||||
309
itest/notification_test.go
Normal file
309
itest/notification_test.go
Normal file
@@ -0,0 +1,309 @@
|
||||
package itest
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/breez/lntest"
|
||||
"github.com/breez/lspd/notifications"
|
||||
lspd "github.com/breez/lspd/rpc"
|
||||
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func testOfflineNotificationPaymentRegistered(p *testParams) {
|
||||
alice := lntest.NewClnNode(p.h, p.m, "Alice")
|
||||
alice.Start()
|
||||
alice.Fund(10000000)
|
||||
p.lsp.LightningNode().Fund(10000000)
|
||||
|
||||
log.Print("Opening channel between Alice and the lsp")
|
||||
channel := alice.OpenChannel(p.lsp.LightningNode(), &lntest.OpenChannelOptions{
|
||||
AmountSat: publicChanAmount,
|
||||
})
|
||||
channelId := alice.WaitForChannelReady(channel)
|
||||
|
||||
log.Printf("Adding bob's invoices")
|
||||
outerAmountMsat := uint64(2100000)
|
||||
innerAmountMsat := calculateInnerAmountMsat(p.lsp, outerAmountMsat, nil)
|
||||
description := "Please pay me"
|
||||
innerInvoice, outerInvoice := GenerateInvoices(p.BreezClient(),
|
||||
generateInvoicesRequest{
|
||||
innerAmountMsat: innerAmountMsat,
|
||||
outerAmountMsat: outerAmountMsat,
|
||||
description: description,
|
||||
lsp: p.lsp,
|
||||
})
|
||||
|
||||
log.Print("Connecting bob to lspd")
|
||||
p.BreezClient().Node().ConnectPeer(p.lsp.LightningNode())
|
||||
|
||||
log.Printf("Registering payment with lsp")
|
||||
RegisterPayment(p.lsp, &lspd.PaymentInformation{
|
||||
PaymentHash: innerInvoice.paymentHash,
|
||||
PaymentSecret: innerInvoice.paymentSecret,
|
||||
Destination: p.BreezClient().Node().NodeId(),
|
||||
IncomingAmountMsat: int64(outerAmountMsat),
|
||||
OutgoingAmountMsat: int64(innerAmountMsat),
|
||||
}, false)
|
||||
|
||||
// Kill the mobile client
|
||||
log.Printf("Stopping breez client")
|
||||
p.BreezClient().Stop()
|
||||
|
||||
port, err := lntest.GetPort()
|
||||
if err != nil {
|
||||
assert.FailNow(p.t, "failed to get port for deliveeery service")
|
||||
}
|
||||
addr := fmt.Sprintf("127.0.0.1:%d", port)
|
||||
delivered := make(chan struct{})
|
||||
|
||||
notify := newNotificationDeliveryService(addr, func(resp http.ResponseWriter, req *http.Request) {
|
||||
var body PaymentReceivedPayload
|
||||
err = json.NewDecoder(req.Body).Decode(&body)
|
||||
assert.NoError(p.t, err)
|
||||
|
||||
ph := hex.EncodeToString(innerInvoice.paymentHash)
|
||||
assert.Equal(p.t, ph, body.Data.PaymentHash)
|
||||
close(delivered)
|
||||
})
|
||||
go notify.Start(p.h.Ctx)
|
||||
go func() {
|
||||
<-delivered
|
||||
log.Printf("Starting breez client again")
|
||||
p.BreezClient().SetHtlcAcceptor(innerAmountMsat)
|
||||
p.BreezClient().Start()
|
||||
p.BreezClient().Node().ConnectPeer(p.lsp.LightningNode())
|
||||
}()
|
||||
|
||||
// TODO: Fix race waiting for htlc interceptor.
|
||||
log.Printf("Waiting %v to allow htlc interceptor to activate.", htlcInterceptorDelay)
|
||||
<-time.After(htlcInterceptorDelay)
|
||||
|
||||
url := "http://" + addr + "/api/v1/notify"
|
||||
first := sha256.Sum256([]byte(url))
|
||||
second := sha256.Sum256(first[:])
|
||||
sig, err := ecdsa.SignCompact(p.BreezClient().Node().PrivateKey(), second[:], true)
|
||||
assert.NoError(p.t, err)
|
||||
|
||||
p.lsp.NotificationsRpc().SubscribeNotifications(p.h.Ctx, ¬ifications.SubscribeNotificationsRequest{
|
||||
Url: url,
|
||||
Signature: sig,
|
||||
})
|
||||
log.Printf("Alice paying")
|
||||
route := constructRoute(p.lsp.LightningNode(), p.BreezClient().Node(), channelId, lntest.NewShortChanIDFromString("1x0x0"), outerAmountMsat)
|
||||
_, err = alice.PayViaRoute(outerAmountMsat, outerInvoice.paymentHash, outerInvoice.paymentSecret, route)
|
||||
assert.Nil(p.t, err)
|
||||
}
|
||||
|
||||
func testOfflineNotificationRegularForward(p *testParams) {
|
||||
alice := lntest.NewClnNode(p.h, p.m, "Alice")
|
||||
alice.Start()
|
||||
alice.Fund(10000000)
|
||||
p.lsp.LightningNode().Fund(10000000)
|
||||
p.BreezClient().Node().Fund(100000)
|
||||
|
||||
log.Print("Opening channel between Alice and the lsp")
|
||||
channelAL := alice.OpenChannel(p.lsp.LightningNode(), &lntest.OpenChannelOptions{
|
||||
AmountSat: publicChanAmount,
|
||||
IsPublic: true,
|
||||
})
|
||||
|
||||
log.Print("Opening channel between lsp and Breez client")
|
||||
channelLB := p.lsp.LightningNode().OpenChannel(p.BreezClient().Node(), &lntest.OpenChannelOptions{
|
||||
AmountSat: 200000,
|
||||
IsPublic: false,
|
||||
})
|
||||
|
||||
log.Print("Waiting for channel between Alice and the lsp to be ready.")
|
||||
alice.WaitForChannelReady(channelAL)
|
||||
log.Print("Waiting for channel between LSP and Bob to be ready.")
|
||||
p.lsp.LightningNode().WaitForChannelReady(channelLB)
|
||||
p.BreezClient().Node().WaitForChannelReady(channelLB)
|
||||
|
||||
port, err := lntest.GetPort()
|
||||
if err != nil {
|
||||
assert.FailNow(p.t, "failed to get port for deliveeery service")
|
||||
}
|
||||
addr := fmt.Sprintf("127.0.0.1:%d", port)
|
||||
delivered := make(chan struct{})
|
||||
|
||||
notify := newNotificationDeliveryService(addr, func(resp http.ResponseWriter, req *http.Request) {
|
||||
var body PaymentReceivedPayload
|
||||
err = json.NewDecoder(req.Body).Decode(&body)
|
||||
assert.NoError(p.t, err)
|
||||
|
||||
close(delivered)
|
||||
})
|
||||
go notify.Start(p.h.Ctx)
|
||||
go func() {
|
||||
<-delivered
|
||||
log.Printf("Notification was delivered. Starting breez client again")
|
||||
p.BreezClient().Start()
|
||||
p.BreezClient().Node().ConnectPeer(p.lsp.LightningNode())
|
||||
}()
|
||||
|
||||
url := "http://" + addr + "/api/v1/notify"
|
||||
first := sha256.Sum256([]byte(url))
|
||||
second := sha256.Sum256(first[:])
|
||||
sig, err := ecdsa.SignCompact(p.BreezClient().Node().PrivateKey(), second[:], true)
|
||||
assert.NoError(p.t, err)
|
||||
|
||||
p.lsp.NotificationsRpc().SubscribeNotifications(p.h.Ctx, ¬ifications.SubscribeNotificationsRequest{
|
||||
Url: url,
|
||||
Signature: sig,
|
||||
})
|
||||
|
||||
<-time.After(time.Second * 2)
|
||||
log.Printf("Adding bob's invoice")
|
||||
amountMsat := uint64(2100000)
|
||||
bobInvoice := p.BreezClient().Node().CreateBolt11Invoice(&lntest.CreateInvoiceOptions{
|
||||
AmountMsat: amountMsat,
|
||||
IncludeHopHints: true,
|
||||
})
|
||||
log.Printf(bobInvoice.Bolt11)
|
||||
|
||||
log.Printf("Bob going offline")
|
||||
p.BreezClient().Stop()
|
||||
|
||||
// TODO: Fix race waiting for htlc interceptor.
|
||||
log.Printf("Waiting %v to allow htlc interceptor to activate.", htlcInterceptorDelay)
|
||||
<-time.After(htlcInterceptorDelay)
|
||||
|
||||
log.Printf("Alice paying")
|
||||
payResp := alice.Pay(bobInvoice.Bolt11)
|
||||
invoiceResult := p.BreezClient().Node().GetInvoice(bobInvoice.PaymentHash)
|
||||
|
||||
assert.Equal(p.t, payResp.PaymentPreimage, invoiceResult.PaymentPreimage)
|
||||
assert.Equal(p.t, amountMsat, invoiceResult.AmountReceivedMsat)
|
||||
}
|
||||
|
||||
func testOfflineNotificationZeroConfChannel(p *testParams) {
|
||||
alice := lntest.NewClnNode(p.h, p.m, "Alice")
|
||||
alice.Start()
|
||||
alice.Fund(10000000)
|
||||
p.lsp.LightningNode().Fund(10000000)
|
||||
|
||||
log.Print("Opening channel between Alice and the lsp")
|
||||
channel := alice.OpenChannel(p.lsp.LightningNode(), &lntest.OpenChannelOptions{
|
||||
AmountSat: publicChanAmount,
|
||||
IsPublic: true,
|
||||
})
|
||||
channelId := alice.WaitForChannelReady(channel)
|
||||
|
||||
log.Printf("Adding bob's invoices")
|
||||
outerAmountMsat := uint64(2100000)
|
||||
innerAmountMsat := calculateInnerAmountMsat(p.lsp, outerAmountMsat, nil)
|
||||
description := "Please pay me"
|
||||
innerInvoice, outerInvoice := GenerateInvoices(p.BreezClient(),
|
||||
generateInvoicesRequest{
|
||||
innerAmountMsat: innerAmountMsat,
|
||||
outerAmountMsat: outerAmountMsat,
|
||||
description: description,
|
||||
lsp: p.lsp,
|
||||
})
|
||||
|
||||
log.Print("Connecting bob to lspd")
|
||||
p.BreezClient().Node().ConnectPeer(p.lsp.LightningNode())
|
||||
p.BreezClient().SetHtlcAcceptor(innerAmountMsat)
|
||||
|
||||
// TODO: Fix race waiting for htlc interceptor.
|
||||
log.Printf("Waiting %v to allow htlc interceptor to activate.", htlcInterceptorDelay)
|
||||
<-time.After(htlcInterceptorDelay)
|
||||
|
||||
log.Printf("Registering payment with lsp")
|
||||
RegisterPayment(p.lsp, &lspd.PaymentInformation{
|
||||
PaymentHash: innerInvoice.paymentHash,
|
||||
PaymentSecret: innerInvoice.paymentSecret,
|
||||
Destination: p.BreezClient().Node().NodeId(),
|
||||
IncomingAmountMsat: int64(outerAmountMsat),
|
||||
OutgoingAmountMsat: int64(innerAmountMsat),
|
||||
}, false)
|
||||
|
||||
expectedheight := p.Miner().GetBlockHeight()
|
||||
log.Printf("Alice paying")
|
||||
route := constructRoute(p.lsp.LightningNode(), p.BreezClient().Node(), channelId, lntest.NewShortChanIDFromString("1x0x0"), outerAmountMsat)
|
||||
_, err := alice.PayViaRoute(outerAmountMsat, outerInvoice.paymentHash, outerInvoice.paymentSecret, route)
|
||||
assert.Nil(p.t, err)
|
||||
|
||||
<-time.After(time.Second * 2)
|
||||
log.Printf("Adding bob's invoice for zero conf payment")
|
||||
amountMsat := uint64(2100000)
|
||||
|
||||
bobInvoice := p.BreezClient().Node().CreateBolt11Invoice(&lntest.CreateInvoiceOptions{
|
||||
AmountMsat: amountMsat,
|
||||
IncludeHopHints: true,
|
||||
})
|
||||
|
||||
invoiceWithHint := bobInvoice.Bolt11
|
||||
if !ContainsHopHint(p.t, bobInvoice.Bolt11) {
|
||||
chans := p.BreezClient().Node().GetChannels()
|
||||
assert.Len(p.t, chans, 1)
|
||||
|
||||
var id lntest.ShortChannelID
|
||||
if chans[0].RemoteAlias != nil {
|
||||
id = *chans[0].RemoteAlias
|
||||
} else if chans[0].LocalAlias != nil {
|
||||
id = *chans[0].LocalAlias
|
||||
} else {
|
||||
id = chans[0].ShortChannelID
|
||||
}
|
||||
invoiceWithHint = AddHopHint(p.BreezClient(), bobInvoice.Bolt11, p.Lsp(), id, nil)
|
||||
}
|
||||
|
||||
log.Printf("Invoice with hint: %s", invoiceWithHint)
|
||||
|
||||
// Kill the mobile client
|
||||
log.Printf("Stopping breez client")
|
||||
p.BreezClient().Stop()
|
||||
p.BreezClient().ResetHtlcAcceptor()
|
||||
|
||||
port, err := lntest.GetPort()
|
||||
if err != nil {
|
||||
assert.FailNow(p.t, "failed to get port for delivery service")
|
||||
}
|
||||
addr := fmt.Sprintf("127.0.0.1:%d", port)
|
||||
delivered := make(chan struct{})
|
||||
|
||||
notify := newNotificationDeliveryService(addr, func(resp http.ResponseWriter, req *http.Request) {
|
||||
var body PaymentReceivedPayload
|
||||
err = json.NewDecoder(req.Body).Decode(&body)
|
||||
assert.NoError(p.t, err)
|
||||
|
||||
close(delivered)
|
||||
})
|
||||
go notify.Start(p.h.Ctx)
|
||||
go func() {
|
||||
<-delivered
|
||||
log.Printf("Starting breez client again")
|
||||
p.BreezClient().Start()
|
||||
p.BreezClient().Node().ConnectPeer(p.lsp.LightningNode())
|
||||
}()
|
||||
|
||||
url := "http://" + addr + "/api/v1/notify"
|
||||
first := sha256.Sum256([]byte(url))
|
||||
second := sha256.Sum256(first[:])
|
||||
sig, err := ecdsa.SignCompact(p.BreezClient().Node().PrivateKey(), second[:], true)
|
||||
assert.NoError(p.t, err)
|
||||
|
||||
p.lsp.NotificationsRpc().SubscribeNotifications(p.h.Ctx, ¬ifications.SubscribeNotificationsRequest{
|
||||
Url: url,
|
||||
Signature: sig,
|
||||
})
|
||||
|
||||
log.Printf("Alice paying zero conf invoice")
|
||||
payResp := alice.Pay(invoiceWithHint)
|
||||
invoiceResult := p.BreezClient().Node().GetInvoice(bobInvoice.PaymentHash)
|
||||
|
||||
assert.Equal(p.t, payResp.PaymentPreimage, invoiceResult.PaymentPreimage)
|
||||
assert.Equal(p.t, amountMsat, invoiceResult.AmountReceivedMsat)
|
||||
|
||||
// Make sure we haven't accidentally mined blocks in between.
|
||||
actualheight := p.Miner().GetBlockHeight()
|
||||
assert.Equal(p.t, expectedheight, actualheight)
|
||||
}
|
||||
Reference in New Issue
Block a user