Skip to content

Commit

Permalink
min/max payment size in fee params in lsps2
Browse files Browse the repository at this point in the history
This change splits the logic for fee params generation between lsps2 and the
grpc protocol, because it adds two fields to the opening fee params. These
fields would otherwise not be able to be added without breaking existing
clients, or having to trust clients to pass the right parameters. Making these
parameters available only in lsps2 gives a clear upgrade path.
  • Loading branch information
JssDWt committed Feb 27, 2024
1 parent ba45715 commit 04f18aa
Show file tree
Hide file tree
Showing 19 changed files with 468 additions and 130 deletions.
7 changes: 3 additions & 4 deletions cmd/lspd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ func main() {
notificationService := notifications.NewNotificationService(notificationsStore)
go notificationService.Start(ctx)
openingService := common.NewOpeningService(openingStore, nodesService)
lsps2OpeningService := lsps2.NewOpeningService(openingStore)
lsps2CleanupService := lsps2.NewCleanupService(lsps2Store)
go lsps2CleanupService.Start(ctx)
notificationCleanupService := notifications.NewCleanupService(notificationsStore)
Expand Down Expand Up @@ -149,14 +150,12 @@ func main() {
forwardSync := cln.NewForwardSync(node.NodeId, client, historyStore)
go forwardSync.ForwardsSynchronize(ctx)
legacyHandler := interceptor.NewInterceptHandler(client, node.NodeConfig, interceptStore, historyStore, openingService, feeEstimator, feeStrategy, notificationService)
lsps2Handler := lsps2.NewInterceptHandler(lsps2Store, historyStore, openingService, client, feeEstimator, &lsps2.InterceptorConfig{
lsps2Handler := lsps2.NewInterceptHandler(lsps2Store, historyStore, lsps2OpeningService, client, feeEstimator, &lsps2.InterceptorConfig{
NodeId: node.NodeId,
AdditionalChannelCapacitySat: uint64(node.NodeConfig.AdditionalChannelCapacity),
MinConfs: node.NodeConfig.MinConfs,
TargetConf: node.NodeConfig.TargetConf,
FeeStrategy: feeStrategy,
MinPaymentSizeMsat: node.NodeConfig.MinPaymentSizeMsat,
MaxPaymentSizeMsat: node.NodeConfig.MaxPaymentSizeMsat,
TimeLockDelta: node.NodeConfig.TimeLockDelta,
HtlcMinimumMsat: node.NodeConfig.MinHtlcMsat,
MppTimeout: time.Second * 90,
Expand All @@ -172,7 +171,7 @@ func main() {
go msgClient.Start()
msgServer := lsps0.NewServer()
protocolServer := lsps0.NewProtocolServer([]uint32{2})
lsps2Server := lsps2.NewLsps2Server(openingService, nodesService, node, lsps2Store)
lsps2Server := lsps2.NewLsps2Server(lsps2OpeningService, nodesService, node, lsps2Store)
lsps0.RegisterProtocolServer(msgServer, protocolServer)
lsps2.RegisterLsps2Server(msgServer, lsps2Server)
msgClient.WaitStarted()
Expand Down
19 changes: 14 additions & 5 deletions common/opening_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ import (
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
)

type OpeningFeeParams struct {
MinFeeMsat uint64 `json:"min_msat,string"`
Proportional uint32 `json:"proportional"`
ValidUntil string `json:"valid_until"`
MinLifetime uint32 `json:"max_idle_time"`
MaxClientToSelfDelay uint32 `json:"max_client_to_self_delay"`
Promise string `json:"promise"`
}

type OpeningService interface {
GetFeeParamsMenu(token string, privateKey *btcec.PrivateKey) ([]*OpeningFeeParams, error)
ValidateOpeningFeeParams(params *OpeningFeeParams, publicKey *btcec.PublicKey) bool
Expand Down Expand Up @@ -50,11 +59,11 @@ func (s *openingService) GetFeeParamsMenu(token string, privateKey *btcec.Privat
for _, setting := range settings {
validUntil := time.Now().UTC().Add(setting.Validity)
params := &OpeningFeeParams{
MinFeeMsat: setting.Params.MinFeeMsat,
Proportional: setting.Params.Proportional,
MinFeeMsat: setting.MinFeeMsat,
Proportional: setting.Proportional,
ValidUntil: validUntil.Format(lsps0.TIME_FORMAT),
MinLifetime: setting.Params.MinLifetime,
MaxClientToSelfDelay: setting.Params.MaxClientToSelfDelay,
MinLifetime: setting.MinLifetime,
MaxClientToSelfDelay: setting.MaxClientToSelfDelay,
}

promise, err := createPromise(privateKey, params)
Expand Down Expand Up @@ -109,7 +118,7 @@ func (s *openingService) IsCurrentChainFeeCheaper(token string, params *OpeningF
}

for _, setting := range settings {
if setting.Params.MinFeeMsat <= params.MinFeeMsat {
if setting.MinFeeMsat <= params.MinFeeMsat {
return true
}
}
Expand Down
18 changes: 7 additions & 11 deletions common/opening_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,13 @@ package common
import "time"

type OpeningFeeParamsSetting struct {
Validity time.Duration
Params *OpeningFeeParams
}

type OpeningFeeParams struct {
MinFeeMsat uint64 `json:"min_msat,string"`
Proportional uint32 `json:"proportional"`
ValidUntil string `json:"valid_until"`
MinLifetime uint32 `json:"max_idle_time"`
MaxClientToSelfDelay uint32 `json:"max_client_to_self_delay"`
Promise string `json:"promise"`
Validity time.Duration
MinFeeMsat uint64
Proportional uint32
MinLifetime uint32
MaxClientToSelfDelay uint32
MinPaymentSizeMsat uint64
MaxPaymentSizeMsat uint64
}

type OpeningStore interface {
Expand Down
8 changes: 0 additions & 8 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,6 @@ type NodeConfig struct {
// peer is offline. Defaults to 1m.
NotificationTimeout string `json:"notificationTimeout,string"`

// The minimum payment size accepted in LSPS2 forwards that need a channel
// open.
MinPaymentSizeMsat uint64 `json:"minPaymentSizeMsat,string"`

// The maximum payment size accepted in LSPS2 forwards that need a channel
// open.
MaxPaymentSizeMsat uint64 `json:"maxPaymentSizeMsat,string"`

// Set this field to connect to an LND node.
Lnd *LndConfig `json:"lnd,omitempty"`

Expand Down
28 changes: 21 additions & 7 deletions itest/lspd_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,6 @@ 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 Expand Up @@ -206,10 +204,18 @@ func (l *lspBase) Initialize() error {

_, err = l.postgresBackend.Pool().Exec(
l.harness.Ctx,
`INSERT INTO new_channel_params (validity, params, token)
`INSERT INTO new_channel_params (
token
, validity
, min_fee_msat
, proportional
, min_lifetime
, max_client_to_self_delay
, min_payment_size_msat
, max_payment_size_msat)
VALUES
(3600, '{"min_msat": "1000000", "proportional": 7500, "max_idle_time": 4320, "max_client_to_self_delay": 432}', 'hello'),
(259200, '{"min_msat": "1100000", "proportional": 7500, "max_idle_time": 4320, "max_client_to_self_delay": 432}', 'hello');`,
('hello', 3600, 1000000, 7500, 4320, 432, 1000, 4000000000),
('hello', 259200, 1100000, 7500, 4320, 432, 1000, 4000000000);`,
)
if err != nil {
lntest.PerformCleanup(cleanups)
Expand Down Expand Up @@ -337,15 +343,23 @@ func SetFeeParams(l LspNode, settings []*FeeParamSetting) error {
return nil
}

query := `INSERT INTO new_channel_params (validity, params, token) VALUES `
query := `INSERT INTO new_channel_params (
token
, validity
, min_fee_msat
, proportional
, min_lifetime
, max_client_to_self_delay
, min_payment_size_msat
, max_payment_size_msat) VALUES `
first := true
for _, setting := range settings {
if !first {
query += `,`
}

query += fmt.Sprintf(
`(%d, '{"min_msat": "%d", "proportional": %d, "max_idle_time": 4320, "max_client_to_self_delay": 432}', 'hello')`,
`('hello', %d, %d, %d, 4320, 432, 1000, 4000000000)`,
int64(setting.Validity.Seconds()),
setting.MinMsat,
setting.Proportional,
Expand Down
6 changes: 3 additions & 3 deletions itest/lsps2_buy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,13 @@ func testLsps2Buy(p *testParams) {
ValidUntil string `json:"valid_until"`
MinLifetime uint32 `json:"min_lifetime"`
MaxClientToSelfDelay uint32 `json:"max_client_to_self_delay"`
MinPaymentSizeMsat uint64 `json:"min_payment_size_msat,string"`
MaxPaymentSizeMsat uint64 `json:"max_payment_size_msat,string"`
Promise string `json:"promise"`
}
data := new(struct {
Result struct {
Menu []params `json:"opening_fee_params_menu"`
MinPayment uint64 `json:"min_payment_size_msat,string"`
MaxPayment uint64 `json:"max_payment_size_msat,string"`
Menu []params `json:"opening_fee_params_menu"`
} `json:"result"`
})
err := json.Unmarshal(resp.Data[:], &data)
Expand Down
6 changes: 5 additions & 1 deletion itest/lsps2_get_info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,16 @@ func testLsps2GetInfo(p *testParams) {
ValidUntil string `json:"valid_until"`
MinLifetime uint32 `json:"min_lifetime"`
MaxClientToSelfDelay uint32 `json:"max_client_to_self_delay"`
MinPaymentSizeMsat uint64 `json:"min_payment_size_msat,string"`
MaxPaymentSizeMsat uint64 `json:"max_payment_size_msat,string"`
Promise string `json:"promise"`
}{}
err = json.Unmarshal(result["opening_fee_params_menu"], &menu)
lntest.CheckError(p.t, err)

assert.Len(p.t, menu, 2)
if !assert.Len(p.t, menu, 2) {
return
}
assert.Equal(p.t, uint64(2000000), menu[0].MinFeeMsat)
assert.Equal(p.t, uint64(3000000), menu[1].MinFeeMsat)
}
11 changes: 5 additions & 6 deletions lsps2/intercept_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ type InterceptorConfig struct {
MinConfs *uint32
TargetConf uint32
FeeStrategy chain.FeeStrategy
MinPaymentSizeMsat uint64
MaxPaymentSizeMsat uint64
TimeLockDelta uint32
HtlcMinimumMsat uint64
MppTimeout time.Duration
Expand All @@ -32,7 +30,7 @@ type InterceptorConfig struct {
type Interceptor struct {
store Lsps2Store
historyStore history.Store
openingService common.OpeningService
openingService OpeningService
client lightning.Client
feeEstimator chain.FeeEstimator
config *InterceptorConfig
Expand All @@ -47,7 +45,7 @@ type Interceptor struct {
func NewInterceptHandler(
store Lsps2Store,
historyStore history.Store,
openingService common.OpeningService,
openingService OpeningService,
client lightning.Client,
feeEstimator chain.FeeEstimator,
config *InterceptorConfig,
Expand Down Expand Up @@ -239,8 +237,8 @@ func (i *Interceptor) processPart(payment *paymentState, part *partState) {
payment.paymentSizeMsat = part.req.OutgoingAmountMsat

// Make sure the minimum and maximum are not exceeded.
if payment.paymentSizeMsat > i.config.MaxPaymentSizeMsat ||
payment.paymentSizeMsat < i.config.MinPaymentSizeMsat {
if payment.paymentSizeMsat > payment.registration.OpeningFeeParams.MaxPaymentSizeMsat ||
payment.paymentSizeMsat < payment.registration.OpeningFeeParams.MinPaymentSizeMsat {
i.failPart(payment, part, common.FAILURE_UNKNOWN_NEXT_PEER)
return
}
Expand Down Expand Up @@ -402,6 +400,7 @@ func (i *Interceptor) ensureChannelOpen(payment *paymentState) {
// they're not cheaper now, fail the payment.
if time.Now().After(validUntil) &&
!i.openingService.IsCurrentChainFeeCheaper(
context.TODO(),
payment.registration.Token,
&payment.registration.OpeningFeeParams,
) {
Expand Down
12 changes: 6 additions & 6 deletions lsps2/intercept_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,15 @@ var defaultChanResult = &lightning.GetChannelResult{
ConfirmedScid: (*lightning.ShortChannelID)(&defaultChannelScid),
}

func defaultOpeningFeeParams() common.OpeningFeeParams {
return common.OpeningFeeParams{
func defaultOpeningFeeParams() OpeningFeeParams {
return OpeningFeeParams{
MinFeeMsat: 1000,
Proportional: 1000,
ValidUntil: time.Now().UTC().Add(5 * time.Hour).Format(lsps0.TIME_FORMAT),
MinLifetime: 1000,
MaxClientToSelfDelay: 2016,
MinPaymentSizeMsat: 1000,
MaxPaymentSizeMsat: 4_000_000_000,
Promise: "fake",
}
}
Expand Down Expand Up @@ -96,8 +98,6 @@ func defaultConfig() *InterceptorConfig {
MinConfs: &minConfs,
TargetConf: 6,
FeeStrategy: chain.FeeStrategyEconomy,
MinPaymentSizeMsat: 1_000,
MaxPaymentSizeMsat: 4_000_000_000,
TimeLockDelta: 144,
HtlcMinimumMsat: 100,
}
Expand Down Expand Up @@ -295,7 +295,7 @@ func Test_NoMpp_AmtAtMaximum(t *testing.T) {
defer cancel()
i := setupInterceptor(ctx, nil)

res := i.Intercept(createPart(&part{amt: defaultConfig().MaxPaymentSizeMsat}))
res := i.Intercept(createPart(&part{amt: defaultOpeningFeeParams().MaxPaymentSizeMsat}))
assert.Equal(t, common.INTERCEPT_RESUME_WITH_ONION, res.Action)
assertEmpty(t, i)
}
Expand All @@ -307,7 +307,7 @@ func Test_NoMpp_AmtAboveMaximum(t *testing.T) {
defer cancel()
i := setupInterceptor(ctx, nil)

res := i.Intercept(createPart(&part{amt: defaultConfig().MaxPaymentSizeMsat + 1}))
res := i.Intercept(createPart(&part{amt: defaultOpeningFeeParams().MaxPaymentSizeMsat + 1}))
assert.Equal(t, common.INTERCEPT_FAIL_HTLC_WITH_CODE, res.Action)
assert.Equal(t, common.FAILURE_UNKNOWN_NEXT_PEER, res.FailureCode)
assertEmpty(t, i)
Expand Down
13 changes: 9 additions & 4 deletions lsps2/mocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,29 +31,31 @@ func (m *mockNodesService) GetNodes() []*common.Node {
}

type mockOpeningService struct {
menu []*common.OpeningFeeParams
menu []*OpeningFeeParams
err error
invalid bool
isCurrentChainFeeCheaper bool
}

func (m *mockOpeningService) GetFeeParamsMenu(
ctx context.Context,
token string,
privateKey *btcec.PrivateKey,
) ([]*common.OpeningFeeParams, error) {
) ([]*OpeningFeeParams, error) {
return m.menu, m.err
}

func (m *mockOpeningService) ValidateOpeningFeeParams(
params *common.OpeningFeeParams,
params *OpeningFeeParams,
publicKey *btcec.PublicKey,
) bool {
return !m.invalid
}

func (m *mockOpeningService) IsCurrentChainFeeCheaper(
ctx context.Context,
token string,
params *common.OpeningFeeParams,
params *OpeningFeeParams,
) bool {
return m.isCurrentChainFeeCheaper
}
Expand Down Expand Up @@ -96,6 +98,9 @@ func (s *mockLsps2Store) SavePromises(ctx context.Context, req *SavePromises) er
func (s *mockLsps2Store) RemoveUnusedExpired(ctx context.Context, before time.Time) error {
return nil
}
func (s *mockLsps2Store) GetFeeParamsSettings(ctx context.Context, token string) ([]*OpeningFeeParamsSetting, error) {
return nil, ErrNotImplemented
}

type mockHistoryStore struct{}

Expand Down
Loading

0 comments on commit 04f18aa

Please sign in to comment.