Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/integration_tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ on:
pull_request:
env:
BITCOIN_VERSION: '25.0'
LSP_REF: 'breez-node-v0.18.3-beta'
LSP_REF: 'breez-node-v0.20.0-beta'
CLIENT_REF: 'v0.17.5-breez-3'
GO_VERSION: '^1.21.4'
GO_VERSION: '^1.24.9'
CLN_VERSION: 'v25.05'

concurrency:
Expand Down
6 changes: 5 additions & 1 deletion cln/cln_interceptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,11 @@ func (i *ClnHtlcInterceptor) resumeWithOnion(request *proto.HtlcAccepted, interc

newPayloadStr := hex.EncodeToString(newPayload)

chanId := lnwire.NewChanIDFromOutPoint(interceptResult.ChannelPoint).String()
if interceptResult.ChannelPoint == nil {
log.Printf("paymenthash: %s, failed to forward htlc. Onion intercept result does not contain channelpoint", request.Htlc.PaymentHash)
return i.failWithCode(request, common.FAILURE_TEMPORARY_CHANNEL_FAILURE)
}
chanId := lnwire.NewChanIDFromOutPoint(*interceptResult.ChannelPoint).String()
log.Printf("paymenthash: %s, forwarding htlc to the destination node and a new private channel was opened", request.Htlc.PaymentHash)
return &proto.HtlcResolution{
Correlationid: request.Correlationid,
Expand Down
6 changes: 1 addition & 5 deletions common/opening_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,7 @@ func createPromise(lspPrivateKey *btcec.PrivateKey, params *OpeningFeeParams) (*
return nil, err
}
// Sign the hash with the private key of the LSP id.
sig, err := ecdsa.SignCompact(lspPrivateKey, hash[:], true)
if err != nil {
log.Printf("createPromise: SignCompact error: %v", err)
return nil, err
}
sig := ecdsa.SignCompact(lspPrivateKey, hash[:], true)
promise := hex.EncodeToString(sig)
return &promise, nil
}
Expand Down
208 changes: 111 additions & 97 deletions go.mod

Large diffs are not rendered by default.

504 changes: 258 additions & 246 deletions go.sum

Large diffs are not rendered by default.

7 changes: 3 additions & 4 deletions itest/breez_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,15 +110,14 @@ func AddHopHint(n BreezClient, invoice string, lsp LspNode, chanid lntest.ShortC
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)
sig := ecdsa.SignCompact(n.Node().PrivateKey(), hash[:], true)
log.Printf(
"sign outer invoice. msg: '%x', hash: '%x', sig: '%x', err: %v",
"sign outer invoice. msg: '%x', hash: '%x', sig: '%x'",
msg,
hash,
sig,
err,
)
return sig, err
return sig, nil
},
})
lntest.CheckError(n.Harness().T, err)
Expand Down
45 changes: 33 additions & 12 deletions itest/lntest/lnd_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,13 @@ func (n *LndNode) Start() {
sig = syscall.SIGKILL
}

return syscall.Kill(-proc.Pid, sig)
err := syscall.Kill(-proc.Pid, sig)
if err != nil {
log.Printf("%s: Error sending signal: %v", n.name, err)
}
}

// Wait for the process to fully exit
<-done
return nil
},
Expand Down Expand Up @@ -204,23 +208,25 @@ func (n *LndNode) Start() {
PerformCleanup(cleanups)
n.harness.T.Fatalf("%s: failed to create grpc connection: %v", n.name, err)
}
defer tmpConn.Close()

err = n.waitServerStarted(tmpConn)
if err != nil {
tmpConn.Close()
PerformCleanup(cleanups)
n.harness.T.Fatalf("%s: waitServerStarted: %v", n.name, err)
}

if n.isInitialized {
err = n.unlockWallet(tmpConn)
if err != nil {
if err != nil && !strings.Contains(err.Error(), "already unlocked") {
tmpConn.Close()
PerformCleanup(cleanups)
n.harness.T.Fatalf("%s: unlockWallet: %v", n.name, err)
}
} else {
mac, priv, _, err := n.initWallet(tmpConn)
if err != nil {
tmpConn.Close()
PerformCleanup(cleanups)
n.harness.T.Fatalf("%s: initWallet: %v", n.name, err)
}
Expand All @@ -229,7 +235,19 @@ func (n *LndNode) Start() {
n.isInitialized = true
}

err = n.waitServerActive(tmpConn)
// Close the temporary connection after unlock/init, as the WalletUnlocker service
// becomes unavailable after the wallet is unlocked
tmpConn.Close()

// Create a new connection to wait for the server to become active
tmpConn2, err := grpc.DialContext(n.harness.Ctx, n.grpcAddress, opts...)
if err != nil {
PerformCleanup(cleanups)
n.harness.T.Fatalf("%s: failed to create grpc connection: %v", n.name, err)
}
defer tmpConn2.Close()

err = n.waitServerActive(tmpConn2)
if err != nil {
PerformCleanup(cleanups)
n.harness.T.Fatalf("%s: waitServerActive: %v", n.name, err)
Expand Down Expand Up @@ -552,19 +570,22 @@ func (n *LndNode) Pay(bolt11 string) *PayResult {
})
CheckError(n.harness.T, err)

if resp.PaymentRoute == nil {
n.harness.T.Fatal(fmt.Errorf("missing payment route after pay"))
var amountMsat uint64
var amountSentMsat uint64
var dest []byte
if resp.PaymentRoute != nil {
lastHop := resp.PaymentRoute.Hops[len(resp.PaymentRoute.Hops)-1]
dest, err = hex.DecodeString(lastHop.PubKey)
CheckError(n.harness.T, err)
amountMsat = uint64(lastHop.AmtToForwardMsat)
amountSentMsat = uint64(resp.PaymentRoute.TotalAmtMsat)
}

lastHop := resp.PaymentRoute.Hops[len(resp.PaymentRoute.Hops)-1]
dest, err := hex.DecodeString(lastHop.PubKey)
CheckError(n.harness.T, err)

return &PayResult{
PaymentHash: resp.PaymentHash,
AmountMsat: uint64(lastHop.AmtToForwardMsat),
AmountMsat: amountMsat,
Destination: dest,
AmountSentMsat: uint64(resp.PaymentRoute.TotalAmtMsat),
AmountSentMsat: amountSentMsat,
PaymentPreimage: resp.PaymentPreimage,
}
}
Expand Down
7 changes: 2 additions & 5 deletions itest/lspd_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import (
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
"github.com/decred/dcrd/dcrec/secp256k1/v4"
ecies "github.com/ecies/go/v2"
"github.com/stretchr/testify/assert"
"github.com/tv42/zbase32"
"google.golang.org/grpc/metadata"
"google.golang.org/protobuf/proto"
Expand Down Expand Up @@ -406,8 +405,7 @@ func SubscribeNotifications(l LspNode, b BreezClient, url string, continueOnErro
msg := append(lightning.SignedMsgPrefix, []byte(url)...)
first := sha256.Sum256([]byte(msg))
second := sha256.Sum256(first[:])
sig, err := ecdsa.SignCompact(b.Node().PrivateKey(), second[:], true)
assert.NoError(b.Harness().T, err)
sig := ecdsa.SignCompact(b.Node().PrivateKey(), second[:], true)
request := notifications.SubscribeNotificationsRequest{
Url: url,
Signature: zbase32.EncodeToString(sig),
Expand Down Expand Up @@ -438,8 +436,7 @@ func UnsubscribeNotifications(l LspNode, b BreezClient, url string, continueOnEr
msg := append(lightning.SignedMsgPrefix, []byte(url)...)
first := sha256.Sum256([]byte(msg))
second := sha256.Sum256(first[:])
sig, err := ecdsa.SignCompact(b.Node().PrivateKey(), second[:], true)
assert.NoError(b.Harness().T, err)
sig := ecdsa.SignCompact(b.Node().PrivateKey(), second[:], true)
request := notifications.UnsubscribeNotificationsRequest{
Url: url,
Signature: zbase32.EncodeToString(sig),
Expand Down
11 changes: 10 additions & 1 deletion itest/notification_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ func testOfflineNotificationPaymentRegistered(p *testParams) {
}
addr := fmt.Sprintf("127.0.0.1:%d", port)
delivered := make(chan struct{})
started := make(chan struct{})

notify := newNotificationDeliveryService(addr, func(resp http.ResponseWriter, req *http.Request) {
var body PaymentReceivedPayload
Expand All @@ -76,6 +77,7 @@ func testOfflineNotificationPaymentRegistered(p *testParams) {
p.BreezClient().SetHtlcAcceptor(innerAmountMsat)
p.BreezClient().Start()
p.BreezClient().Node().ConnectPeer(p.lsp.LightningNode())
close(started)
}()

// TODO: Fix race waiting for htlc interceptor.
Expand All @@ -88,6 +90,7 @@ func testOfflineNotificationPaymentRegistered(p *testParams) {
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)
<-started // Wait for breez-client goroutine to complete before test ends
}

func testOfflineNotificationRegularForward(p *testParams) {
Expand Down Expand Up @@ -121,6 +124,7 @@ func testOfflineNotificationRegularForward(p *testParams) {
}
addr := fmt.Sprintf("127.0.0.1:%d", port)
delivered := make(chan struct{})
started := make(chan struct{})

notify := newNotificationDeliveryService(addr, func(resp http.ResponseWriter, req *http.Request) {
var body PaymentReceivedPayload
Expand All @@ -135,6 +139,7 @@ func testOfflineNotificationRegularForward(p *testParams) {
log.Printf("Notification was delivered. Starting breez client again")
p.BreezClient().Start()
// p.BreezClient().Node().ConnectPeer(p.lsp.LightningNode())
close(started)
}()

url := "http://" + addr + "/api/v1/notify"
Expand All @@ -147,7 +152,7 @@ func testOfflineNotificationRegularForward(p *testParams) {
AmountMsat: amountMsat,
IncludeHopHints: true,
})
log.Printf(bobInvoice.Bolt11)
log.Printf("%s", bobInvoice.Bolt11)

invoiceWithHint := bobInvoice.Bolt11
if !ContainsHopHint(p.t, bobInvoice.Bolt11) {
Expand Down Expand Up @@ -175,6 +180,7 @@ func testOfflineNotificationRegularForward(p *testParams) {

log.Printf("Alice paying")
payResp := alice.Pay(invoiceWithHint)
<-started // Wait for breez-client to be fully started
invoiceResult := p.BreezClient().Node().GetInvoice(bobInvoice.PaymentHash)

assert.Equal(p.t, payResp.PaymentPreimage, invoiceResult.PaymentPreimage)
Expand Down Expand Up @@ -267,6 +273,7 @@ func testOfflineNotificationZeroConfChannel(p *testParams) {
}
addr := fmt.Sprintf("127.0.0.1:%d", port)
delivered := make(chan struct{})
started := make(chan struct{})

notify := newNotificationDeliveryService(addr, func(resp http.ResponseWriter, req *http.Request) {
var body PaymentReceivedPayload
Expand All @@ -281,13 +288,15 @@ func testOfflineNotificationZeroConfChannel(p *testParams) {
log.Printf("Starting breez client again")
p.BreezClient().Start()
p.BreezClient().Node().ConnectPeer(p.lsp.LightningNode())
close(started)
}()

url := "http://" + addr + "/api/v1/notify"
SubscribeNotifications(p.lsp, p.BreezClient(), url, false)

log.Printf("Alice paying zero conf invoice")
payResp := alice.Pay(invoiceWithHint)
<-started // Wait for breez-client to be fully started
invoiceResult := p.BreezClient().Node().GetInvoice(bobInvoice.PaymentHash)

assert.Equal(p.t, payResp.PaymentPreimage, invoiceResult.PaymentPreimage)
Expand Down
23 changes: 12 additions & 11 deletions itest/postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import (
"time"

"github.com/breez/lspd/itest/lntest"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/client"
"github.com/docker/go-connections/nat"
"github.com/jackc/pgx/v5/pgxpool"
Expand Down Expand Up @@ -61,6 +61,7 @@ func (c *PostgresContainer) Start(ctx context.Context) error {
if err != nil {
return fmt.Errorf("could not create docker client: %w", err)
}
c.cli.NegotiateAPIVersion(ctx)

if !c.isInitialized {
err := c.initialize(ctx)
Expand All @@ -70,7 +71,7 @@ func (c *PostgresContainer) Start(ctx context.Context) error {
}
}

err = c.cli.ContainerStart(ctx, c.id, types.ContainerStartOptions{})
err = c.cli.ContainerStart(ctx, c.id, container.StartOptions{})
if err != nil {
c.cli.Close()
return fmt.Errorf("failed to start docker container '%s': %w", c.id, err)
Expand Down Expand Up @@ -119,27 +120,27 @@ HealthCheck:
}

func (c *PostgresContainer) initialize(ctx context.Context) error {
image := "postgres:15"
_, _, err := c.cli.ImageInspectWithRaw(ctx, image)
imageTag := "postgres:15"
_, _, err := c.cli.ImageInspectWithRaw(ctx, imageTag)
if err != nil {
if !client.IsErrNotFound(err) {
return fmt.Errorf("could not find docker image '%s': %w", image, err)
return fmt.Errorf("could not find docker image '%s': %w", imageTag, err)
}

pullReader, err := c.cli.ImagePull(ctx, image, types.ImagePullOptions{})
pullReader, err := c.cli.ImagePull(ctx, imageTag, image.PullOptions{})
if err != nil {
return fmt.Errorf("failed to pull docker image '%s': %w", image, err)
return fmt.Errorf("failed to pull docker image '%s': %w", imageTag, err)
}
defer pullReader.Close()

_, err = io.Copy(io.Discard, pullReader)
if err != nil {
return fmt.Errorf("failed to download docker image '%s': %w", image, err)
return fmt.Errorf("failed to download docker image '%s': %w", imageTag, err)
}
}

createResp, err := c.cli.ContainerCreate(ctx, &container.Config{
Image: image,
Image: imageTag,
Cmd: []string{
"postgres",
"-c",
Expand Down Expand Up @@ -204,13 +205,13 @@ func (c *PostgresContainer) Cleanup(ctx context.Context) error {
return err
}
defer cli.Close()
return cli.ContainerRemove(ctx, c.id, types.ContainerRemoveOptions{
return cli.ContainerRemove(ctx, c.id, container.RemoveOptions{
Force: true,
})
}

func (c *PostgresContainer) monitorLogs(ctx context.Context) {
i, err := c.cli.ContainerLogs(ctx, c.id, types.ContainerLogsOptions{
i, err := c.cli.ContainerLogs(ctx, c.id, container.LogsOptions{
ShowStderr: true,
ShowStdout: true,
Timestamps: false,
Expand Down
3 changes: 3 additions & 0 deletions itest/probing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ func testProbing(p *testParams) {
log.Printf("Stopping breez client")
p.BreezClient().Stop()

// Wait a bit to ensure the client is fully offline
<-time.After(2 * time.Second)

log.Printf("Alice paying with fake payment hash with Bob offline %x", fakePaymentHash)
_, err = alice.PayViaRoute(outerAmountMsat, fakePaymentHash, outerInvoice.paymentSecret, route)

Expand Down
2 changes: 1 addition & 1 deletion itest/regular_forward_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func testRegularForward(p *testParams) {
AmountMsat: amountMsat,
IncludeHopHints: true,
})
log.Printf(bobInvoice.Bolt11)
log.Printf("%s", bobInvoice.Bolt11)

invoiceWithHint := bobInvoice.Bolt11
if !ContainsHopHint(p.t, bobInvoice.Bolt11) {
Expand Down
3 changes: 0 additions & 3 deletions lnd/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,9 +303,6 @@ func (c *LndClient) GetChannel(peerID []byte, channelPoint wire.OutPoint) (*ligh
}

channelPointStr := channelPoint.String()
if err != nil {
return nil, err
}

for _, c := range r.Channels {
log.Printf("getChannel(%x): %v", peerID, c.ChanId)
Expand Down
2 changes: 1 addition & 1 deletion lnd/interceptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ func (i *LndHtlcInterceptor) constructOnion(
}

var b bytes.Buffer
err = hop.PackHopPayload(&b, uint64(0))
err = hop.PackHopPayload(&b, uint64(0), true)
if err != nil {
log.Printf("hop.PackHopPayload(): %v", err)
return nil, err
Expand Down
Loading