Skip to content

Commit

Permalink
lsps2: add integration tests
Browse files Browse the repository at this point in the history
  • Loading branch information
JssDWt committed Oct 20, 2023
1 parent 5c33909 commit 71c8ea5
Show file tree
Hide file tree
Showing 11 changed files with 466 additions and 22 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/integration_tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,11 @@ jobs:
testLsps0GetProtocolVersions,
testLsps2GetVersions,
testLsps2GetInfo,
testLsps2Buy
testLsps2Buy,
testLsps2HappyFlow,
testLsps2Cltv,
testLsps2NoBalance,
testLsps2ZeroConfUtxo
]
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
50 changes: 41 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,44 @@ var allTestCases = []*testCase{
test: testOfflineNotificationZeroConfChannel,
},
{
name: "testLsps0GetProtocolVersions",
test: testLsps0GetProtocolVersions,
name: "testLsps0GetProtocolVersions",
test: testLsps0GetProtocolVersions,
isLsps2: true,
},
{
name: "testLsps2GetVersions",
test: testLsps2GetVersions,
isLsps2: true,
},
{
name: "testLsps2GetInfo",
test: testLsps2GetInfo,
isLsps2: true,
},
{
name: "testLsps2GetVersions",
test: testLsps2GetVersions,
name: "testLsps2Buy",
test: testLsps2Buy,
isLsps2: true,
},
{
name: "testLsps2GetInfo",
test: testLsps2GetInfo,
name: "testLsps2HappyFlow",
test: testLsps2HappyFlow,
isLsps2: true,
},
{
name: "testLsps2Buy",
test: testLsps2Buy,
name: "testLsps2Cltv",
test: testLsps2Cltv,
isLsps2: true,
},
{
name: "testLsps2NoBalance",
test: testLsps2NoBalance,
isLsps2: true,
},
{
name: "testLsps2ZeroConfUtxo",
test: testLsps2ZeroConfUtxo,
isLsps2: true,
skipCreateLsp: true,
},
}
44 changes: 40 additions & 4 deletions itest/lsps2_buy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import (
"time"

"github.com/breez/lntest"
"github.com/breez/lspd/lightning"
"github.com/breez/lspd/lsps0"
"github.com/jackc/pgx/v4/pgxpool"
"github.com/stretchr/testify/assert"
)

func testLsps2Buy(p *testParams) {
Expand Down Expand Up @@ -62,20 +65,20 @@ func testLsps2Buy(p *testParams) {
Type: lsps0.Lsps0MessageType,
Data: append(append(
[]byte(`{
"method": "lsps2.get_info",
"method": "lsps2.buy",
"jsonrpc": "2.0",
"id": "example#3cad6a54d302edba4c9ade2f7ffac098",
"id": "example#3cad6a54d302edba4c9ade2f7ffac099",
"params": {
"version": 1,
"payment_size_msat": "42000",
"payment_size_msat": "42000000",
"opening_fee_params":`),
pr...),
[]byte(`}}`)...,
),
})

buyResp := p.BreezClient().ReceiveCustomMessage()
log.Print(string(resp.Data))
log.Print(string(buyResp.Data))
b := new(struct {
Result struct {
Jit_channel_scid string `json:"jit_channel_scid"`
Expand All @@ -85,4 +88,37 @@ func testLsps2Buy(p *testParams) {
})
err = json.Unmarshal(buyResp.Data, b)
lntest.CheckError(p.t, err)

pgxPool, err := pgxpool.Connect(p.h.Ctx, p.lsp.PostgresBackend().ConnectionString())
if err != nil {
p.h.T.Fatalf("Failed to connect to postgres backend: %v", err)
}
defer pgxPool.Close()

scid, err := lightning.NewShortChannelIDFromString(b.Result.Jit_channel_scid)
lntest.CheckError(p.t, err)

rows, err := pgxPool.Query(
p.h.Ctx,
`SELECT token
FROM lsps2.buy_registrations
WHERE scid = $1`,
int64(uint64(*scid)),
)
if err != nil {
p.h.T.Fatalf("Failed to query token: %v", err)
}

defer rows.Close()
if !rows.Next() {
p.h.T.Fatal("No rows found")
}

var actual string
err = rows.Scan(&actual)
if err != nil {
p.h.T.Fatalf("Failed to get token from row: %v", err)
}

assert.Equal(p.h.T, "hello", actual)
}
63 changes: 63 additions & 0 deletions itest/lsps2_cltv_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package itest

import (
"log"
"time"

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

func testLsps2Cltv(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,
})
aliceLspScid := 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")
route := constructRoute(p.Lsp().LightningNode(), p.BreezClient().Node(), aliceLspScid, lntest.NewShortChanIDFromString(buyResp.JitChannelScid), outerAmountMsat)

// Increment the delay by one (should be incremented by 2), so the cltv delta is too little.
route.Hops[0].Delay++
_, err := alice.PayViaRoute(outerAmountMsat, outerInvoice.paymentHash, outerInvoice.paymentSecret, route)
assert.Contains(p.t, err.Error(), "WIRE_INCORRECT_CLTV_EXPIRY")
}
Loading

0 comments on commit 71c8ea5

Please sign in to comment.