Skip to content

Commit

Permalink
lsps2: add first integration test
Browse files Browse the repository at this point in the history
  • Loading branch information
JssDWt committed Sep 4, 2023
1 parent dd4cf5d commit 8f0e251
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 18 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/integration_tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ jobs:
testLsps0GetProtocolVersions,
testLsps2GetVersions,
testLsps2GetInfo,
testLsps2Buy
testLsps2Buy,
testLsps2HappyFlow
]
implementation: [
CLN
Expand Down
92 changes: 85 additions & 7 deletions itest/breez_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@ package itest

import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"log"
"math/rand"
"testing"

"github.com/breez/lntest"
"github.com/breez/lspd/lsps0"
"github.com/breez/lspd/lsps0/jsonrpc"
"github.com/breez/lspd/lsps2"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
"github.com/btcsuite/btcd/chaincfg"
Expand Down Expand Up @@ -39,6 +45,18 @@ type invoice struct {
}

func GenerateInvoices(n BreezClient, req generateInvoicesRequest) (invoice, invoice) {
return generateInvoices(n, req, lntest.ShortChannelID{
BlockHeight: 1,
TxIndex: 0,
OutputIndex: 0,
}, lspCltvDelta)
}

func GenerateLsps2Invoices(n BreezClient, req generateInvoicesRequest, scid string) (invoice, invoice) {
return generateInvoices(n, req, lntest.NewShortChanIDFromString(scid), lspCltvDelta+2)
}

func generateInvoices(n BreezClient, req generateInvoicesRequest, scid lntest.ShortChannelID, cltvDelta uint16) (invoice, invoice) {
preimage, err := GenerateRandomBytes(32)
lntest.CheckError(n.Harness().T, err)

Expand All @@ -47,11 +65,7 @@ func GenerateInvoices(n BreezClient, req generateInvoicesRequest) (invoice, invo
Description: &req.description,
Preimage: &preimage,
})
outerInvoice := AddHopHint(n, innerInvoice.Bolt11, req.lsp, lntest.ShortChannelID{
BlockHeight: 1,
TxIndex: 0,
OutputIndex: 0,
}, &req.outerAmountMsat)
outerInvoice := AddHopHint(n, innerInvoice.Bolt11, req.lsp, scid, &req.outerAmountMsat, cltvDelta)

inner := invoice{
bolt11: innerInvoice.Bolt11,
Expand All @@ -76,7 +90,7 @@ func ContainsHopHint(t *testing.T, invoice string) bool {
return len(rawInvoice.RouteHints) > 0
}

func AddHopHint(n BreezClient, invoice string, lsp LspNode, chanid lntest.ShortChannelID, amountMsat *uint64) string {
func AddHopHint(n BreezClient, invoice string, lsp LspNode, chanid lntest.ShortChannelID, amountMsat *uint64, cltvDelta uint16) string {
rawInvoice, err := zpay32.Decode(invoice, &chaincfg.RegressionNetParams)
lntest.CheckError(n.Harness().T, err)

Expand All @@ -93,7 +107,7 @@ func AddHopHint(n BreezClient, invoice string, lsp LspNode, chanid lntest.ShortC
ChannelID: chanid.ToUint64(),
FeeBaseMSat: lspBaseFeeMsat,
FeeProportionalMillionths: lspFeeRatePpm,
CLTVExpiryDelta: lspCltvDelta,
CLTVExpiryDelta: cltvDelta,
},
})

Expand Down Expand Up @@ -121,3 +135,67 @@ func AddHopHint(n BreezClient, invoice string, lsp LspNode, chanid lntest.ShortC

return newInvoice
}

func Lsps2GetInfo(c BreezClient, l LspNode, req lsps2.GetInfoRequest) lsps2.GetInfoResponse {
req.Version = lsps2.SupportedVersion
r := lsps2RequestResponse(c, l, "lsps2.get_info", req)
var resp lsps2.GetInfoResponse
err := json.Unmarshal(r, &resp)
lntest.CheckError(c.Harness().T, err)

return resp
}

func Lsps2Buy(c BreezClient, l LspNode, req lsps2.BuyRequest) lsps2.BuyResponse {
req.Version = lsps2.SupportedVersion
r := lsps2RequestResponse(c, l, "lsps2.buy", req)
var resp lsps2.BuyResponse
err := json.Unmarshal(r, &resp)
lntest.CheckError(c.Harness().T, err)

return resp
}

func lsps2RequestResponse(c BreezClient, l LspNode, method string, req interface{}) []byte {
id := RandStringBytes(32)
peerId := hex.EncodeToString(l.NodeId())
inner, err := json.Marshal(req)
lntest.CheckError(c.Harness().T, err)
outer, err := json.Marshal(&jsonrpc.Request{
JsonRpc: jsonrpc.Version,
Method: method,
Id: id,
Params: inner,
})
lntest.CheckError(c.Harness().T, err)

log.Printf(string(outer))
c.Node().SendCustomMessage(&lntest.CustomMsgRequest{
PeerId: peerId,
Type: lsps0.Lsps0MessageType,
Data: outer,
})

m := c.ReceiveCustomMessage()
log.Printf(string(m.Data))

var resp jsonrpc.Response
err = json.Unmarshal(m.Data, &resp)
lntest.CheckError(c.Harness().T, err)

if resp.Id != id {
c.Harness().T.Fatalf("Received custom message, but had different id")
}

return resp.Result
}

const letterBytes = "abcdefghijklmnopqrstuvwxyz"

func RandStringBytes(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand.Intn(len(letterBytes))]
}
return string(b)
}
2 changes: 2 additions & 0 deletions itest/lspd_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ func newLspd(h *lntest.TestHarness, mem *mempoolApi, name string, nodeConfig *co
ChannelMinimumFeeMsat: 2000000,
AdditionalChannelCapacity: 100000,
MaxInactiveDuration: 3888000,
MinPaymentSizeMsat: 600,
MaxPaymentSizeMsat: 4000000000,
Lnd: lnd,
Cln: cln,
}
Expand Down
34 changes: 25 additions & 9 deletions itest/lspd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@ func TestLspd(t *testing.T) {
return
}
testCases := allTestCases
runTests(t, testCases, "LND-lspd", lndLspFunc, lndClientFunc)
var lndTestCases []*testCase
for _, c := range testCases {
if !c.isLsps2 {
lndTestCases = append(lndTestCases, c)
}
}
runTests(t, lndTestCases, "LND-lspd", lndLspFunc, lndClientFunc)
runTests(t, testCases, "CLN-lspd", clnLspFunc, clnClientFunc)
}

Expand Down Expand Up @@ -106,6 +112,7 @@ type testCase struct {
name string
test func(t *testParams)
skipCreateLsp bool
isLsps2 bool
timeout time.Duration
}

Expand Down Expand Up @@ -168,19 +175,28 @@ var allTestCases = []*testCase{
test: testOfflineNotificationZeroConfChannel,
},
{
name: "testLsps0GetProtocolVersions",
test: testLsps0GetProtocolVersions,
name: "testLsps0GetProtocolVersions",
test: testLsps0GetProtocolVersions,
isLsps2: true,
},
{
name: "testLsps2GetVersions",
test: testLsps2GetVersions,
isLsps2: true,
},
{
name: "testLsps2GetVersions",
test: testLsps2GetVersions,
name: "testLsps2GetInfo",
test: testLsps2GetInfo,
isLsps2: true,
},
{
name: "testLsps2GetInfo",
test: testLsps2GetInfo,
name: "testLsps2Buy",
test: testLsps2Buy,
isLsps2: true,
},
{
name: "testLsps2Buy",
test: testLsps2Buy,
name: "testLsps2HappyFlow",
test: testLsps2HappyFlow,
isLsps2: true,
},
}
68 changes: 68 additions & 0 deletions itest/lsps2_happy_flow_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package itest

import (
"log"
"time"

"github.com/breez/lntest"
"github.com/breez/lspd/lsps2"
"github.com/stretchr/testify/assert"
)

func testLsps2HappyFlow(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,
})
alice.WaitForChannelReady(channel)

log.Print("Connecting bob to lspd")
p.BreezClient().Node().ConnectPeer(p.lsp.LightningNode())

log.Printf("Calling lsps2.get_info")
info := Lsps2GetInfo(p.BreezClient(), p.Lsp(), lsps2.GetInfoRequest{
Token: &WorkingToken,
})

outerAmountMsat := uint64(2100000)
innerAmountMsat := lsps2CalculateInnerAmountMsat(p.lsp, outerAmountMsat, info.OpeningFeeParamsMenu[0])
p.BreezClient().SetHtlcAcceptor(innerAmountMsat)

log.Printf("Calling lsps2.buy")
buyResp := Lsps2Buy(p.BreezClient(), p.Lsp(), lsps2.BuyRequest{
OpeningFeeParams: *info.OpeningFeeParamsMenu[0],
PaymentSizeMsat: &outerAmountMsat,
})

log.Printf("Adding bob's invoices")
description := "Please pay me"
_, outerInvoice := GenerateLsps2Invoices(p.BreezClient(),
generateInvoicesRequest{
innerAmountMsat: innerAmountMsat,
outerAmountMsat: outerAmountMsat,
description: description,
lsp: p.lsp,
},
buyResp.JitChannelScid)

// 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().Node().GetInvoice(payResp.PaymentHash)

assert.Equal(p.t, payResp.PaymentPreimage, bobInvoice.PaymentPreimage)
assert.Equal(p.t, innerAmountMsat, bobInvoice.AmountReceivedMsat)

// Make sure capacity is correct
chans := p.BreezClient().Node().GetChannels()
assert.Equal(p.t, 1, len(chans))
c := chans[0]
AssertChannelCapacity(p.t, innerAmountMsat, c.CapacityMsat)
}
2 changes: 1 addition & 1 deletion itest/notification_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ func testOfflineNotificationZeroConfChannel(p *testParams) {
} else {
id = chans[0].ShortChannelID
}
invoiceWithHint = AddHopHint(p.BreezClient(), bobInvoice.Bolt11, p.Lsp(), id, nil)
invoiceWithHint = AddHopHint(p.BreezClient(), bobInvoice.Bolt11, p.Lsp(), id, nil, lspCltvDelta)
}

log.Printf("Invoice with hint: %s", invoiceWithHint)
Expand Down
13 changes: 13 additions & 0 deletions itest/test_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ import (
"log"
"testing"

"github.com/breez/lspd/lsps2"
lspd "github.com/breez/lspd/rpc"
"github.com/stretchr/testify/assert"
)

var WorkingToken = "hello"

func GenerateRandomBytes(n int) ([]byte, error) {
b := make([]byte, n)
_, err := rand.Read(b)
Expand Down Expand Up @@ -51,4 +54,14 @@ func calculateInnerAmountMsat(lsp LspNode, outerAmountMsat uint64, params *lspd.
return outerAmountMsat - fee
}

func lsps2CalculateInnerAmountMsat(lsp LspNode, outerAmountMsat uint64, params *lsps2.OpeningFeeParams) uint64 {
fee := (outerAmountMsat*uint64(params.Proportional) + 999_999) / 1_000_000
if fee < params.MinFeeMsat {
fee = params.MinFeeMsat
}

log.Printf("outer: %v, fee: %v", outerAmountMsat, fee)
return outerAmountMsat - fee
}

var publicChanAmount uint64 = 1000183

0 comments on commit 8f0e251

Please sign in to comment.