lsps2: implement get_info

This commit is contained in:
Jesse de Wit
2023-08-12 10:45:13 +02:00
parent c61741baa2
commit b406d8ea53
7 changed files with 336 additions and 7 deletions

View File

@@ -143,7 +143,8 @@ jobs:
matrix:
test: [
testLsps0GetProtocolVersions,
testLsps2GetVersions
testLsps2GetVersions,
testLsps2GetInfo
]
implementation: [
CLN

View File

@@ -75,6 +75,9 @@ type NodeConfig struct {
// peer is offline.
NotificationTimeout string `json:"notificationTimeout,string"`
MinPaymentSizeMsat uint64 `json:"minPaymentSizeMsat,string"`
MaxPaymentSizeMsat uint64 `json:"maxPaymentSizeMsat,string"`
// Set this field to connect to an LND node.
Lnd *LndConfig `json:"lnd,omitempty"`

View File

@@ -182,4 +182,8 @@ var allTestCases = []*testCase{
name: "testLsps2GetVersions",
test: testLsps2GetVersions,
},
{
name: "testLsps2GetInfo",
test: testLsps2GetInfo,
},
}

View File

@@ -0,0 +1,70 @@
package itest
import (
"encoding/hex"
"encoding/json"
"log"
"time"
"github.com/breez/lntest"
"github.com/breez/lspd/lsps0"
"github.com/stretchr/testify/assert"
)
func testLsps2GetInfo(p *testParams) {
SetFeeParams(p.Lsp(), []*FeeParamSetting{
{
Validity: time.Second * 3600,
MinMsat: 3000000,
Proportional: 1000,
},
{
Validity: time.Second * 2800,
MinMsat: 2000000,
Proportional: 800,
},
})
p.BreezClient().Node().ConnectPeer(p.Lsp().LightningNode())
rawMsg := `{
"method": "lsps2.get_info",
"jsonrpc": "2.0",
"id": "example#3cad6a54d302edba4c9ade2f7ffac098",
"params": {
"version": 1,
"token": "hello"
}
}`
p.BreezClient().Node().SendCustomMessage(&lntest.CustomMsgRequest{
PeerId: hex.EncodeToString(p.Lsp().NodeId()),
Type: lsps0.Lsps0MessageType,
Data: []byte(rawMsg),
})
resp := p.BreezClient().ReceiveCustomMessage()
log.Print(string(resp.Data))
assert.Equal(p.t, uint32(37913), resp.Type)
content := make(map[string]json.RawMessage)
err := json.Unmarshal(resp.Data[:], &content)
lntest.CheckError(p.t, err)
result := make(map[string]json.RawMessage)
err = json.Unmarshal(content["result"], &result)
lntest.CheckError(p.t, err)
menu := []*struct {
MinFeeMsat uint64 `json:"min_fee_msat,string"`
Proportional uint32 `json:"proportional"`
ValidUntil string `json:"valid_until"`
MinLifetime uint32 `json:"min_lifetime"`
MaxClientToSelfDelay uint32 `json:"max_client_to_self_delay"`
Promise string `json:"promise"`
}{}
err = json.Unmarshal(result["opening_fee_params_menu"], &menu)
lntest.CheckError(p.t, err)
assert.Len(p.t, menu, 2)
assert.Equal(p.t, uint64(2000000), menu[0].MinFeeMsat)
assert.Equal(p.t, uint64(3000000), menu[1].MinFeeMsat)
}

View File

@@ -2,24 +2,63 @@ package lsps2
import (
"context"
"log"
"github.com/breez/lspd/lsps0"
"github.com/breez/lspd/lsps0/codes"
"github.com/breez/lspd/lsps0/status"
"github.com/breez/lspd/shared"
)
var SupportedVersion uint32 = 1
type GetVersionsRequest struct {
}
type GetVersionsResponse struct {
Versions []int32 `json:"versions"`
Versions []uint32 `json:"versions"`
}
type GetInfoRequest struct {
Version uint32 `json:"version"`
Token *string `json:"token,omitempty"`
}
type GetInfoResponse struct {
OpeningFeeParamsMenu []*OpeningFeeParams `json:"opening_fee_params_menu"`
MinPaymentSizeMsat uint64 `json:"min_payment_size_msat,string"`
MaxPaymentSizeMsat uint64 `json:"max_payment_size_msat,string"`
}
type OpeningFeeParams struct {
MinFeeMsat uint64 `json:"min_fee_msat,string"`
Proportional uint32 `json:"proportional"`
ValidUntil string `json:"valid_until"`
MinLifetime uint32 `json:"min_lifetime"`
MaxClientToSelfDelay uint32 `json:"max_client_to_self_delay"`
Promise string `json:"promise"`
}
type Lsps2Server interface {
GetVersions(ctx context.Context, request *GetVersionsRequest) (*GetVersionsResponse, error)
GetInfo(ctx context.Context, request *GetInfoRequest) (*GetInfoResponse, error)
}
type server struct {
openingService shared.OpeningService
nodesService shared.NodesService
node *shared.Node
}
type server struct{}
func NewLsps2Server() Lsps2Server {
return &server{}
func NewLsps2Server(
openingService shared.OpeningService,
nodesService shared.NodesService,
node *shared.Node,
) Lsps2Server {
return &server{
openingService: openingService,
nodesService: nodesService,
node: node,
}
}
func (s *server) GetVersions(
@@ -27,7 +66,64 @@ func (s *server) GetVersions(
request *GetVersionsRequest,
) (*GetVersionsResponse, error) {
return &GetVersionsResponse{
Versions: []int32{1},
Versions: []uint32{SupportedVersion},
}, nil
}
func (s *server) GetInfo(
ctx context.Context,
request *GetInfoRequest,
) (*GetInfoResponse, error) {
if request.Version != uint32(SupportedVersion) {
return nil, status.New(codes.Code(1), "unsupported_version").Err()
}
if request.Token == nil || *request.Token == "" {
return nil, status.New(codes.Code(2), "unrecognized_or_stale_token").Err()
}
node, err := s.nodesService.GetNode(*request.Token)
if err == shared.ErrNodeNotFound {
return nil, status.New(codes.Code(2), "unrecognized_or_stale_token").Err()
}
if err != nil {
log.Printf("Lsps2Server.GetInfo: nodesService.GetNode(%s) err: %v", *request.Token, err)
return nil, status.New(codes.InternalError, "internal error").Err()
}
if node.NodeConfig.NodePubkey != s.node.NodeConfig.NodePubkey {
log.Printf(
"Lsps2Server.GetInfo: Got token '%s' on node '%s', but was meant for node '%s'",
*request.Token,
s.node.NodeConfig.NodePubkey,
node.NodeConfig.NodePubkey,
)
return nil, status.New(codes.Code(2), "unrecognized_or_stale_token").Err()
}
m, err := s.openingService.GetFeeParamsMenu(*request.Token, node.PrivateKey)
if err == shared.ErrNodeNotFound {
return nil, status.New(codes.Code(2), "unrecognized_or_stale_token").Err()
}
if err != nil {
log.Printf("Lsps2Server.GetInfo: openingService.GetFeeParamsMenu(%s) err: %v", *request.Token, err)
return nil, status.New(codes.InternalError, "internal error").Err()
}
menu := []*OpeningFeeParams{}
for _, p := range m {
menu = append(menu, &OpeningFeeParams{
MinFeeMsat: p.MinFeeMsat,
Proportional: p.Proportional,
ValidUntil: p.ValidUntil,
MinLifetime: p.MinLifetime,
MaxClientToSelfDelay: p.MaxClientToSelfDelay,
Promise: p.Promise,
})
}
return &GetInfoResponse{
OpeningFeeParamsMenu: menu,
MinPaymentSizeMsat: node.NodeConfig.MinPaymentSizeMsat,
MaxPaymentSizeMsat: node.NodeConfig.MaxPaymentSizeMsat,
}, nil
}
@@ -47,6 +143,16 @@ func RegisterLsps2Server(s lsps0.ServiceRegistrar, l Lsps2Server) {
return srv.(Lsps2Server).GetVersions(ctx, in)
},
},
{
MethodName: "lsps2.get_info",
Handler: func(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) {
in := new(GetInfoRequest)
if err := dec(in); err != nil {
return nil, err
}
return srv.(Lsps2Server).GetInfo(ctx, in)
},
},
},
},
l,

145
lsps2/server_test.go Normal file
View File

@@ -0,0 +1,145 @@
package lsps2
import (
"context"
"testing"
"github.com/breez/lspd/config"
"github.com/breez/lspd/lsps0/status"
"github.com/breez/lspd/shared"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/stretchr/testify/assert"
)
type mockNodesService struct {
node *shared.Node
err error
}
func (m *mockNodesService) GetNode(token string) (*shared.Node, error) {
return m.node, m.err
}
func (m *mockNodesService) GetNodes() []*shared.Node {
return []*shared.Node{m.node}
}
type mockOpeningService struct {
menu []*shared.OpeningFeeParams
err error
valid bool
}
func (m *mockOpeningService) GetFeeParamsMenu(
token string,
privateKey *btcec.PrivateKey,
) ([]*shared.OpeningFeeParams, error) {
return m.menu, m.err
}
func (m *mockOpeningService) ValidateOpeningFeeParams(
params *shared.OpeningFeeParams,
publicKey *btcec.PublicKey,
) bool {
return m.valid
}
var token = "blah"
var node = &shared.Node{
NodeConfig: &config.NodeConfig{
MinPaymentSizeMsat: 123,
MaxPaymentSizeMsat: 456,
},
}
func Test_GetInfo_UnsupportedVersion(t *testing.T) {
n := &mockNodesService{}
o := &mockOpeningService{}
s := NewLsps2Server(o, n, nil)
_, err := s.GetInfo(context.Background(), &GetInfoRequest{
Version: 2,
Token: &token,
})
st := status.Convert(err)
assert.Equal(t, uint32(1), uint32(st.Code))
assert.Equal(t, "unsupported_version", st.Message)
}
func Test_GetInfo_InvalidToken(t *testing.T) {
n := &mockNodesService{
err: shared.ErrNodeNotFound,
}
o := &mockOpeningService{}
s := NewLsps2Server(o, n, nil)
_, err := s.GetInfo(context.Background(), &GetInfoRequest{
Version: 1,
Token: &token,
})
st := status.Convert(err)
assert.Equal(t, uint32(2), uint32(st.Code))
assert.Equal(t, "unrecognized_or_stale_token", st.Message)
}
func Test_GetInfo_EmptyMenu(t *testing.T) {
n := &mockNodesService{node: node}
o := &mockOpeningService{menu: []*shared.OpeningFeeParams{}}
s := NewLsps2Server(o, n, node)
resp, err := s.GetInfo(context.Background(), &GetInfoRequest{
Version: 1,
Token: &token,
})
assert.Nil(t, err)
assert.Equal(t, []*OpeningFeeParams{}, resp.OpeningFeeParamsMenu)
assert.Equal(t, node.NodeConfig.MinPaymentSizeMsat, resp.MinPaymentSizeMsat)
assert.Equal(t, node.NodeConfig.MaxPaymentSizeMsat, resp.MaxPaymentSizeMsat)
}
func Test_GetInfo_PopulatedMenu_Ordered(t *testing.T) {
n := &mockNodesService{node: node}
o := &mockOpeningService{menu: []*shared.OpeningFeeParams{
{
MinFeeMsat: 1,
Proportional: 2,
ValidUntil: "a",
MinLifetime: 3,
MaxClientToSelfDelay: 4,
Promise: "b",
},
{
MinFeeMsat: 5,
Proportional: 6,
ValidUntil: "c",
MinLifetime: 7,
MaxClientToSelfDelay: 8,
Promise: "d",
},
}}
s := NewLsps2Server(o, n, node)
resp, err := s.GetInfo(context.Background(), &GetInfoRequest{
Version: 1,
Token: &token,
})
assert.Nil(t, err)
assert.Len(t, resp.OpeningFeeParamsMenu, 2)
assert.Equal(t, uint64(1), resp.OpeningFeeParamsMenu[0].MinFeeMsat)
assert.Equal(t, uint32(2), resp.OpeningFeeParamsMenu[0].Proportional)
assert.Equal(t, "a", resp.OpeningFeeParamsMenu[0].ValidUntil)
assert.Equal(t, uint32(3), resp.OpeningFeeParamsMenu[0].MinLifetime)
assert.Equal(t, uint32(4), resp.OpeningFeeParamsMenu[0].MaxClientToSelfDelay)
assert.Equal(t, "b", resp.OpeningFeeParamsMenu[0].Promise)
assert.Equal(t, uint64(5), resp.OpeningFeeParamsMenu[1].MinFeeMsat)
assert.Equal(t, uint32(6), resp.OpeningFeeParamsMenu[1].Proportional)
assert.Equal(t, "c", resp.OpeningFeeParamsMenu[1].ValidUntil)
assert.Equal(t, uint32(7), resp.OpeningFeeParamsMenu[1].MinLifetime)
assert.Equal(t, uint32(8), resp.OpeningFeeParamsMenu[1].MaxClientToSelfDelay)
assert.Equal(t, "d", resp.OpeningFeeParamsMenu[1].Promise)
assert.Equal(t, node.NodeConfig.MinPaymentSizeMsat, resp.MinPaymentSizeMsat)
assert.Equal(t, node.NodeConfig.MaxPaymentSizeMsat, resp.MaxPaymentSizeMsat)
}

View File

@@ -125,7 +125,7 @@ func main() {
go msgClient.Start()
msgServer := lsps0.NewServer()
protocolServer := lsps0.NewProtocolServer([]uint32{2})
lsps2Server := lsps2.NewLsps2Server()
lsps2Server := lsps2.NewLsps2Server(openingService, nodesService, node)
lsps0.RegisterProtocolServer(msgServer, protocolServer)
lsps2.RegisterLsps2Server(msgServer, lsps2Server)
msgClient.WaitStarted()