diff --git a/.github/workflows/integration_tests.yaml b/.github/workflows/integration_tests.yaml index 66677eb1..1fc4ca0a 100644 --- a/.github/workflows/integration_tests.yaml +++ b/.github/workflows/integration_tests.yaml @@ -124,3 +124,35 @@ jobs: CLIENT_REF: ${{ env.CLIENT_REF }} GO_VERSION: ${{ env.GO_VERSION }} CLN_VERSION: ${{ env.CLN_VERSION }} + + run-lsps2-test: + runs-on: ubuntu-22.04 + needs: + - setup-itest + - setup-bitcoin-core + - setup-cln + - build-lspd + name: test ${{ matrix.implementation }} ${{ matrix.test }} + strategy: + max-parallel: 6 + matrix: + test: [ + testLsps0GetProtocolVersions + ] + implementation: [ + CLN + ] + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Run and Process Test State + uses: ./.github/actions/test-lspd + with: + TESTRE: "TestLspd/${{ matrix.implementation }}-lspd:_${{ matrix.test }}" + artifact-name: TestLspd-${{ matrix.implementation }}-lspd_${{ matrix.test }} + bitcoin-version: ${{ env.BITCOIN_VERSION }} + LSP_REF: ${{ env.LSP_REF }} + CLIENT_REF: ${{ env.CLIENT_REF }} + GO_VERSION: ${{ env.GO_VERSION }} + CLN_VERSION: ${{ env.CLN_VERSION }} diff --git a/go.mod b/go.mod index 4525e3eb..9fe25854 100644 --- a/go.mod +++ b/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.26 + github.com/breez/lntest v0.0.27 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 diff --git a/itest/breez_client.go b/itest/breez_client.go index 80f1fa85..f117b16b 100644 --- a/itest/breez_client.go +++ b/itest/breez_client.go @@ -21,6 +21,7 @@ type BreezClient interface { Stop() error SetHtlcAcceptor(totalMsat uint64) ResetHtlcAcceptor() + ReceiveCustomMessage() *lntest.CustomMsgRequest } type generateInvoicesRequest struct { diff --git a/itest/cln_breez_client.go b/itest/cln_breez_client.go index ee2ad666..9436179d 100644 --- a/itest/cln_breez_client.go +++ b/itest/cln_breez_client.go @@ -4,6 +4,7 @@ import ( "bufio" "bytes" "context" + "encoding/binary" "encoding/hex" "fmt" "io" @@ -51,16 +52,18 @@ python %s ` type clnBreezClient struct { - name string - scriptDir string - pluginFilePath string - htlcAcceptorAddress string - htlcAcceptor func(*proto.HtlcAccepted) *proto.HtlcResolution - htlcAcceptorCancel context.CancelFunc - harness *lntest.TestHarness - isInitialized bool - node *lntest.ClnNode - mtx sync.Mutex + name string + scriptDir string + pluginFilePath string + pluginAddress string + htlcAcceptor func(*proto.HtlcAccepted) *proto.HtlcResolution + htlcAcceptorCancel context.CancelFunc + customMsgCancel context.CancelFunc + customMsgQueue chan *lntest.CustomMsgRequest + harness *lntest.TestHarness + isInitialized bool + node *lntest.ClnNode + mtx sync.Mutex } func newClnBreezClient(h *lntest.TestHarness, m *lntest.Miner, name string) BreezClient { @@ -89,12 +92,12 @@ func newClnBreezClient(h *lntest.TestHarness, m *lntest.Miner, name string) Bree ) return &clnBreezClient{ - name: name, - harness: h, - node: node, - scriptDir: scriptDir, - pluginFilePath: pluginFilePath, - htlcAcceptorAddress: htlcAcceptorAddress, + name: name, + harness: h, + node: node, + scriptDir: scriptDir, + pluginFilePath: pluginFilePath, + pluginAddress: htlcAcceptorAddress, } } @@ -121,6 +124,8 @@ func (c *clnBreezClient) Start() { c.node.Start() c.startHtlcAcceptor() + c.customMsgQueue = make(chan *lntest.CustomMsgRequest, 100) + c.startCustomMsgListener() } func (c *clnBreezClient) ResetHtlcAcceptor() { @@ -202,6 +207,67 @@ func (c *clnBreezClient) SetHtlcAcceptor(totalMsat uint64) { } } +func (c *clnBreezClient) startCustomMsgListener() { + ctx, cancel := context.WithCancel(c.harness.Ctx) + c.customMsgCancel = cancel + + go func() { + for { + if ctx.Err() != nil { + return + } + + select { + case <-ctx.Done(): + return + case <-time.After(time.Second): + } + + conn, err := grpc.DialContext( + ctx, + c.pluginAddress, + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithKeepaliveParams(keepalive.ClientParameters{ + Time: time.Duration(10) * time.Second, + Timeout: time.Duration(10) * time.Second, + }), + ) + if err != nil { + log.Printf("%s: Dial htlc acceptor error: %v", c.name, err) + continue + } + + client := proto.NewClnPluginClient(conn) + listener, err := client.CustomMsgStream(ctx, &proto.CustomMessageRequest{}) + if err != nil { + log.Printf("%s: client.CustomMsgStream() error: %v", c.name, err) + break + } + for { + msg, err := listener.Recv() + if err != nil { + log.Printf("%s: listener.Recv() error: %v", c.name, err) + break + } + + payload, err := hex.DecodeString(msg.Payload) + lntest.CheckError(c.harness.T, err) + + c.customMsgQueue <- &lntest.CustomMsgRequest{ + PeerId: msg.PeerId, + Type: uint32(binary.BigEndian.Uint16(payload)), + Data: payload[2:], + } + } + } + }() +} + +func (c *clnBreezClient) ReceiveCustomMessage() *lntest.CustomMsgRequest { + msg := <-c.customMsgQueue + return msg +} + func (c *clnBreezClient) startHtlcAcceptor() { ctx, cancel := context.WithCancel(c.harness.Ctx) c.htlcAcceptorCancel = cancel @@ -220,7 +286,7 @@ func (c *clnBreezClient) startHtlcAcceptor() { conn, err := grpc.DialContext( ctx, - c.htlcAcceptorAddress, + c.pluginAddress, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithKeepaliveParams(keepalive.ClientParameters{ Time: time.Duration(10) * time.Second, diff --git a/itest/lnd_breez_client.go b/itest/lnd_breez_client.go index c95febd8..c86be474 100644 --- a/itest/lnd_breez_client.go +++ b/itest/lnd_breez_client.go @@ -86,6 +86,11 @@ func (c *lndBreezClient) SetHtlcAcceptor(totalMsat uint64) { // No need for a htlc acceptor in the LND breez client } +func (c *lndBreezClient) ReceiveCustomMessage() *lntest.CustomMsgRequest { + // TODO: Not implemented. + return nil +} + func (c *lndBreezClient) startChannelAcceptor(ctx context.Context) error { client, err := c.node.LightningClient().ChannelAcceptor(ctx) if err != nil { diff --git a/itest/lspd_test.go b/itest/lspd_test.go index 098d68a1..39ad0657 100644 --- a/itest/lspd_test.go +++ b/itest/lspd_test.go @@ -163,4 +163,8 @@ var allTestCases = []*testCase{ name: "testOfflineNotificationZeroConfChannel", test: testOfflineNotificationZeroConfChannel, }, + { + name: "testLsps0GetProtocolVersions", + test: testLsps0GetProtocolVersions, + }, } diff --git a/itest/lsps0_protocol_version_test.go b/itest/lsps0_protocol_version_test.go new file mode 100644 index 00000000..6761b10f --- /dev/null +++ b/itest/lsps0_protocol_version_test.go @@ -0,0 +1,45 @@ +package itest + +import ( + "encoding/hex" + "encoding/json" + + "github.com/breez/lntest" + "github.com/breez/lspd/lsps0" + "github.com/stretchr/testify/assert" +) + +func testLsps0GetProtocolVersions(p *testParams) { + p.BreezClient().Node().ConnectPeer(p.Lsp().LightningNode()) + + rawMsg := `{ + "method": "lsps0.listprotocols", + "jsonrpc": "2.0", + "id": "example#3cad6a54d302edba4c9ade2f7ffac098", + "params": {} + }` + p.BreezClient().Node().SendCustomMessage(&lntest.CustomMsgRequest{ + PeerId: hex.EncodeToString(p.Lsp().NodeId()), + Type: lsps0.Lsps0MessageType, + Data: []byte(rawMsg), + }) + + resp := p.BreezClient().ReceiveCustomMessage() + assert.Equal(p.t, uint32(37913), resp.Type) + + content := make(map[string]interface{}) + err := json.Unmarshal(resp.Data[:], &content) + lntest.CheckError(p.t, err) + + assert.Equal(p.t, "2.0", content["jsonrpc"]) + assert.Equal(p.t, "example#3cad6a54d302edba4c9ade2f7ffac098", content["id"]) + + content2 := make(map[string]json.RawMessage) + err = json.Unmarshal(resp.Data[:], &content2) + lntest.CheckError(p.t, err) + + result := make(map[string][]int) + err = json.Unmarshal(content2["result"], &result) + lntest.CheckError(p.t, err) + assert.Equal(p.t, []int{2}, result["protocols"]) +}