diff --git a/go.mod b/go.mod index bdd0d90..ef373b1 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( github.com/aws/aws-sdk-go v1.30.20 - github.com/breez/lntest v0.0.9 + github.com/breez/lntest v0.0.10 github.com/btcsuite/btcd v0.23.3 github.com/btcsuite/btcd/btcec/v2 v2.2.1 github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 diff --git a/itest/breez_client.go b/itest/breez_client.go index f872ae5..7465c33 100644 --- a/itest/breez_client.go +++ b/itest/breez_client.go @@ -3,11 +3,13 @@ package itest import ( "bufio" "crypto/sha256" + "flag" "fmt" "os" "path/filepath" "github.com/breez/lntest" + "github.com/breez/lntest/lnd" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2/ecdsa" "github.com/btcsuite/btcd/chaincfg" @@ -77,7 +79,7 @@ func newClnBreezClient(h *lntest.TestHarness, m *lntest.Miner, name string) *bre lntest.CheckError(h.T, err) pluginFile.Close() - node := lntest.NewCoreLightningNode( + node := lntest.NewClnNode( h, m, name, @@ -99,8 +101,19 @@ func newClnBreezClient(h *lntest.TestHarness, m *lntest.Miner, name string) *bre } } +var lndMobileExecutable = flag.String( + "lndmobileexec", "", "full path to lnd mobile binary", +) + func newLndBreezClient(h *lntest.TestHarness, m *lntest.Miner, name string) *breezClient { - lnd := lntest.NewLndNode(h, m, name) + lnd := lntest.NewLndNodeFromBinary(h, m, name, *lndMobileExecutable, + "--protocol.zero-conf", + "--protocol.option-scid-alias", + "--bitcoin.defaultchanconfs=0", + ) + + go startChannelAcceptor(h, lnd) + return &breezClient{ name: name, harness: h, @@ -108,6 +121,31 @@ func newLndBreezClient(h *lntest.TestHarness, m *lntest.Miner, name string) *bre } } +func startChannelAcceptor(h *lntest.TestHarness, n *lntest.LndNode) error { + client, err := n.LightningClient().ChannelAcceptor(h.Ctx) + lntest.CheckError(h.T, err) + + for { + request, err := client.Recv() + if err != nil { + return err + } + + private := request.ChannelFlags&uint32(lnwire.FFAnnounceChannel) == 0 + resp := &lnd.ChannelAcceptResponse{ + PendingChanId: request.PendingChanId, + Accept: private, + } + if request.WantsZeroConf { + resp.MinAcceptDepth = 0 + resp.ZeroConf = true + } + + err = client.Send(resp) + lntest.CheckError(h.T, err) + } +} + type generateInvoicesRequest struct { innerAmountMsat uint64 outerAmountMsat uint64 diff --git a/itest/intercept_zero_conf_test.go b/itest/intercept_zero_conf_test.go index 079dcf4..f5d5ab5 100644 --- a/itest/intercept_zero_conf_test.go +++ b/itest/intercept_zero_conf_test.go @@ -2,26 +2,29 @@ package itest import ( "log" + "time" "github.com/breez/lntest" lspd "github.com/breez/lspd/rpc" "github.com/stretchr/testify/assert" ) +var htlcInterceptorDelay = time.Second * 7 + func testOpenZeroConfChannelOnReceive(p *testParams) { - alice := lntest.NewCoreLightningNode(p.h, p.m, "Alice") + alice := lntest.NewClnNode(p.h, p.m, "Alice") 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: 1000000, + AmountSat: publicChanAmount, }) alice.WaitForChannelReady(channel) log.Printf("Adding bob's invoices") outerAmountMsat := uint64(2100000) - innerAmountMsat := uint64(2100000) + innerAmountMsat := calculateInnerAmountMsat(p.lsp, outerAmountMsat) description := "Please pay me" innerInvoice, outerInvoice := p.BreezClient().GenerateInvoices(generateInvoicesRequest{ innerAmountMsat: innerAmountMsat, @@ -44,12 +47,15 @@ func testOpenZeroConfChannelOnReceive(p *testParams) { OutgoingAmountMsat: int64(pretendAmount), }) + // 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(outerInvoice.bolt11) bobInvoice := p.BreezClient().lightningNode.GetInvoice(payResp.PaymentHash) assert.Equal(p.t, payResp.PaymentPreimage, bobInvoice.PaymentPreimage) - assert.Equal(p.t, outerAmountMsat, bobInvoice.AmountReceivedMsat) + assert.Equal(p.t, innerAmountMsat, bobInvoice.AmountReceivedMsat) // Make sure capacity is correct chans := p.BreezClient().lightningNode.GetChannels() @@ -59,20 +65,20 @@ func testOpenZeroConfChannelOnReceive(p *testParams) { } func testOpenZeroConfSingleHtlc(p *testParams) { - alice := lntest.NewCoreLightningNode(p.h, p.m, "Alice") + alice := lntest.NewClnNode(p.h, p.m, "Alice") 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: 1000000, + AmountSat: publicChanAmount, }) channelId := alice.WaitForChannelReady(channel) log.Printf("Adding bob's invoices") outerAmountMsat := uint64(2100000) - innerAmountMsat := uint64(2100000) + innerAmountMsat := calculateInnerAmountMsat(p.lsp, outerAmountMsat) description := "Please pay me" innerInvoice, outerInvoice := p.BreezClient().GenerateInvoices(generateInvoicesRequest{ innerAmountMsat: innerAmountMsat, @@ -95,13 +101,16 @@ func testOpenZeroConfSingleHtlc(p *testParams) { OutgoingAmountMsat: int64(pretendAmount), }) + // 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") route := constructRoute(p.lsp.LightningNode(), p.BreezClient().lightningNode, channelId, lntest.NewShortChanIDFromString("1x0x0"), outerAmountMsat) payResp := alice.PayViaRoute(outerAmountMsat, outerInvoice.paymentHash, outerInvoice.paymentSecret, route) bobInvoice := p.BreezClient().lightningNode.GetInvoice(payResp.PaymentHash) assert.Equal(p.t, payResp.PaymentPreimage, bobInvoice.PaymentPreimage) - assert.Equal(p.t, outerAmountMsat, bobInvoice.AmountReceivedMsat) + assert.Equal(p.t, innerAmountMsat, bobInvoice.AmountReceivedMsat) // Make sure capacity is correct chans := p.BreezClient().lightningNode.GetChannels() diff --git a/itest/lspd_node.go b/itest/lspd_node.go index c5dd445..e845d5b 100644 --- a/itest/lspd_node.go +++ b/itest/lspd_node.go @@ -43,11 +43,12 @@ type LspNode interface { Rpc() lspd.ChannelOpenerClient NodeId() []byte LightningNode() lntest.LightningNode + SupportsChargingFees() bool } type ClnLspNode struct { harness *lntest.TestHarness - lightningNode *lntest.CoreLightningNode + lightningNode *lntest.ClnNode rpc lspd.ChannelOpenerClient publicKey btcec.PublicKey eciesPublicKey ecies.PublicKey @@ -72,11 +73,11 @@ func (c *ClnLspNode) Rpc() lspd.ChannelOpenerClient { func (l *ClnLspNode) TearDown() error { // NOTE: The lightningnode will be torn down on its own. - return l.postgresBackend.Shutdown(l.harness.Ctx) + return l.postgresBackend.Shutdown(context.Background()) } func (l *ClnLspNode) Cleanup() error { - return l.postgresBackend.Cleanup(l.harness.Ctx) + return l.postgresBackend.Cleanup(context.Background()) } func (l *ClnLspNode) NodeId() []byte { @@ -87,6 +88,10 @@ func (l *ClnLspNode) LightningNode() lntest.LightningNode { return l.lightningNode } +func (l *ClnLspNode) SupportsChargingFees() bool { + return false +} + type LndLspNode struct { harness *lntest.TestHarness lightningNode *lntest.LndNode @@ -113,6 +118,9 @@ func (c *LndLspNode) EciesPublicKey() *ecies.PublicKey { func (c *LndLspNode) Rpc() lspd.ChannelOpenerClient { return c.rpc } +func (l *LndLspNode) SupportsChargingFees() bool { + return true +} func (l *LndLspNode) TearDown() error { // NOTE: The lightningnode will be torn down on its own. @@ -130,11 +138,11 @@ func (l *LndLspNode) TearDown() error { } } - return l.postgresBackend.Shutdown(l.harness.Ctx) + return l.postgresBackend.Shutdown(context.Background()) } func (l *LndLspNode) Cleanup() error { - return l.postgresBackend.Cleanup(l.harness.Ctx) + return l.postgresBackend.Cleanup(context.Background()) } func (l *LndLspNode) NodeId() []byte { @@ -156,7 +164,7 @@ func NewClnLspdNode(h *lntest.TestHarness, m *lntest.Miner, name string) LspNode "--dev-allowdustreserve=true", } - lightningNode := lntest.NewCoreLightningNode(h, m, name, args...) + lightningNode := lntest.NewClnNode(h, m, name, args...) conn, err := grpc.Dial( grpcAddress, @@ -187,7 +195,7 @@ func NewLndLspdNode(h *lntest.TestHarness, m *lntest.Miner, name string) LspNode "--protocol.option-scid-alias", "--requireinterceptor", "--bitcoin.defaultchanconfs=0", - "--bitcoin.chanreservescript=\"0\"", + fmt.Sprintf("--bitcoin.chanreservescript=\"0 if (chanAmt != %d) else chanAmt/100\"", publicChanAmount), fmt.Sprintf("--bitcoin.basefee=%d", lspBaseFeeMsat), fmt.Sprintf("--bitcoin.feerate=%d", lspFeeRatePpm), fmt.Sprintf("--bitcoin.timelockdelta=%d", lspCltvDelta), diff --git a/itest/lspd_test.go b/itest/lspd_test.go index bf34d4f..6ae6c37 100644 --- a/itest/lspd_test.go +++ b/itest/lspd_test.go @@ -13,9 +13,9 @@ var defaultTimeout time.Duration = time.Second * 120 func TestLspd(t *testing.T) { testCases := allTestCases - // runTests(t, testCases, "LND-lspd", func(h *lntest.TestHarness, m *lntest.Miner) (LspNode, *breezClient) { - // return NewLndLspdNode(h, m, "lsp"), newLndBreezClient(h, m, "breez-client") - // }) + runTests(t, testCases, "LND-lspd", func(h *lntest.TestHarness, m *lntest.Miner) (LspNode, *breezClient) { + return NewLndLspdNode(h, m, "lsp"), newLndBreezClient(h, m, "breez-client") + }) runTests(t, testCases, "CLN-lspd", func(h *lntest.TestHarness, m *lntest.Miner) (LspNode, *breezClient) { return NewClnLspdNode(h, m, "lsp"), newClnBreezClient(h, m, "breez-client") diff --git a/itest/test_common.go b/itest/test_common.go index 926d673..c83b8fe 100644 --- a/itest/test_common.go +++ b/itest/test_common.go @@ -25,3 +25,18 @@ func AssertChannelCapacity( ) { assert.Equal(t, ((outerAmountMsat/1000)+100000)*1000, capacityMsat) } + +func calculateInnerAmountMsat(lsp LspNode, outerAmountMsat uint64) uint64 { + if lsp.SupportsChargingFees() { + fee := outerAmountMsat * 40 / 10_000 / 1_000 * 1_000 + if fee < 2000000 { + fee = 2000000 + } + + return outerAmountMsat - fee + } else { + return outerAmountMsat + } +} + +var publicChanAmount uint64 = 1000183 diff --git a/itest/zero_reserve_test.go b/itest/zero_reserve_test.go index bc218ce..ea6e8d6 100644 --- a/itest/zero_reserve_test.go +++ b/itest/zero_reserve_test.go @@ -2,6 +2,7 @@ package itest import ( "log" + "time" "github.com/breez/lntest" lspd "github.com/breez/lspd/rpc" @@ -9,19 +10,19 @@ import ( ) func testZeroReserve(p *testParams) { - alice := lntest.NewCoreLightningNode(p.h, p.m, "Alice") + alice := lntest.NewClnNode(p.h, p.m, "Alice") 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: 1000000, + AmountSat: publicChanAmount, }) channelId := alice.WaitForChannelReady(channel) log.Printf("Adding bob's invoices") outerAmountMsat := uint64(2100000) - innerAmountMsat := uint64(2100000) + innerAmountMsat := calculateInnerAmountMsat(p.lsp, outerAmountMsat) description := "Please pay me" innerInvoice, outerInvoice := p.BreezClient().GenerateInvoices(generateInvoicesRequest{ innerAmountMsat: innerAmountMsat, @@ -44,6 +45,9 @@ func testZeroReserve(p *testParams) { OutgoingAmountMsat: int64(pretendAmount), }) + // 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") route := constructRoute(p.lsp.LightningNode(), p.BreezClient().lightningNode, channelId, lntest.NewShortChanIDFromString("1x0x0"), outerAmountMsat) alice.PayViaRoute(outerAmountMsat, outerInvoice.paymentHash, outerInvoice.paymentSecret, route) @@ -54,5 +58,6 @@ func testZeroReserve(p *testParams) { c := chans[0] assert.Equal(p.t, c.RemoteReserveMsat, c.CapacityMsat/100) + log.Printf("local reserve: %d, remote reserve: %d", c.LocalReserveMsat, c.RemoteReserveMsat) assert.Zero(p.t, c.LocalReserveMsat) }