diff --git a/interceptor/store.go b/interceptor/store.go index 3956ec2..a4fd774 100644 --- a/interceptor/store.go +++ b/interceptor/store.go @@ -6,9 +6,18 @@ import ( "github.com/btcsuite/btcd/wire" ) +type OpeningFeeParams struct { + MinMsat uint64 `json:"min_msat,string"` + Proportional uint32 `json:"proportional"` + ValidUntil string `json:"valid_until"` + MaxIdleTime uint32 `json:"max_idle_time"` + MaxClientToSelfDelay uint32 `json:"max_client_to_self_delay"` + Promise string `json:"promise"` +} + type InterceptStore interface { PaymentInfo(htlcPaymentHash []byte) ([]byte, []byte, []byte, int64, int64, *wire.OutPoint, error) SetFundingTx(paymentHash []byte, channelPoint *wire.OutPoint) error - RegisterPayment(destination, paymentHash, paymentSecret []byte, incomingAmountMsat, outgoingAmountMsat int64, tag string) error + RegisterPayment(params *OpeningFeeParams, destination, paymentHash, paymentSecret []byte, incomingAmountMsat, outgoingAmountMsat int64, tag string) error InsertChannel(initialChanID, confirmedChanId uint64, channelPoint string, nodeID []byte, lastUpdate time.Time) error } diff --git a/postgresql/intercept_store.go b/postgresql/intercept_store.go index c3dec86..1796faa 100644 --- a/postgresql/intercept_store.go +++ b/postgresql/intercept_store.go @@ -2,11 +2,13 @@ package postgresql import ( "context" + "encoding/json" "fmt" "log" "time" "github.com/breez/lspd/basetypes" + "github.com/breez/lspd/interceptor" "github.com/btcsuite/btcd/wire" "github.com/jackc/pgtype" "github.com/jackc/pgx/v4" @@ -60,22 +62,29 @@ func (s *PostgresInterceptStore) SetFundingTx(paymentHash []byte, channelPoint * return err } -func (s *PostgresInterceptStore) RegisterPayment(destination, paymentHash, paymentSecret []byte, incomingAmountMsat, outgoingAmountMsat int64, tag string) error { +func (s *PostgresInterceptStore) RegisterPayment(params *interceptor.OpeningFeeParams, destination, paymentHash, paymentSecret []byte, incomingAmountMsat, outgoingAmountMsat int64, tag string) error { var t *string if tag != "" { t = &tag } + + p, err := json.Marshal(params) + if err != nil { + log.Printf("Failed to marshal OpeningFeeParams: %v", err) + return err + } + commandTag, err := s.pool.Exec(context.Background(), `INSERT INTO - payments (destination, payment_hash, payment_secret, incoming_amount_msat, outgoing_amount_msat, tag) - VALUES ($1, $2, $3, $4, $5, $6) + payments (destination, payment_hash, payment_secret, incoming_amount_msat, outgoing_amount_msat, tag, opening_fee_params) + VALUES ($1, $2, $3, $4, $5, $6, $7) ON CONFLICT DO NOTHING`, - destination, paymentHash, paymentSecret, incomingAmountMsat, outgoingAmountMsat, t) - log.Printf("registerPayment(%x, %x, %x, %v, %v, %v) rows: %v err: %v", - destination, paymentHash, paymentSecret, incomingAmountMsat, outgoingAmountMsat, tag, commandTag.RowsAffected(), err) + destination, paymentHash, paymentSecret, incomingAmountMsat, outgoingAmountMsat, t, p) + log.Printf("registerPayment(%x, %x, %x, %v, %v, %v, %s) rows: %v err: %v", + destination, paymentHash, paymentSecret, incomingAmountMsat, outgoingAmountMsat, tag, p, commandTag.RowsAffected(), err) if err != nil { - return fmt.Errorf("registerPayment(%x, %x, %x, %v, %v, %v) error: %w", - destination, paymentHash, paymentSecret, incomingAmountMsat, outgoingAmountMsat, tag, err) + return fmt.Errorf("registerPayment(%x, %x, %x, %v, %v, %v, %s) error: %w", + destination, paymentHash, paymentSecret, incomingAmountMsat, outgoingAmountMsat, tag, p, err) } return nil } diff --git a/postgresql/migrations/000010_opening_fee_params.down.sql b/postgresql/migrations/000010_opening_fee_params.down.sql new file mode 100644 index 0000000..20173eb --- /dev/null +++ b/postgresql/migrations/000010_opening_fee_params.down.sql @@ -0,0 +1 @@ +ALTER TABLE public.payments DROP COLUMN opening_fee_params; diff --git a/postgresql/migrations/000010_opening_fee_params.up.sql b/postgresql/migrations/000010_opening_fee_params.up.sql new file mode 100644 index 0000000..0efe06c --- /dev/null +++ b/postgresql/migrations/000010_opening_fee_params.up.sql @@ -0,0 +1 @@ +ALTER TABLE public.payments ADD opening_fee_params jsonb NULL; diff --git a/server.go b/server.go index d3517e4..361944c 100644 --- a/server.go +++ b/server.go @@ -39,8 +39,6 @@ import ( "github.com/lightningnetwork/lnd/lnwire" ) -var TIME_FORMAT string = "2006-01-02T15:04:05.999Z" - type server struct { lspdrpc.ChannelOpenerServer address string @@ -186,7 +184,10 @@ func validateOpeningFeeParams(node *node, params *lspdrpc.OpeningFeeParams) bool return true } -func (s *server) RegisterPayment(ctx context.Context, in *lspdrpc.RegisterPaymentRequest) (*lspdrpc.RegisterPaymentReply, error) { +func (s *server) RegisterPayment( + ctx context.Context, + in *lspdrpc.RegisterPaymentRequest, +) (*lspdrpc.RegisterPaymentReply, error) { node, err := getNode(ctx) if err != nil { return nil, err @@ -223,12 +224,38 @@ func (s *server) RegisterPayment(ctx context.Context, in *lspdrpc.RegisterPaymen } } - err = checkPayment(node.nodeConfig, pi.IncomingAmountMsat, pi.OutgoingAmountMsat) + // TODO: Remove this nil check and the else cluase when we enforce all + // clients to use opening_fee_params. + if pi.OpeningFeeParams != nil { + valid := validateOpeningFeeParams(node, pi.OpeningFeeParams) + if !valid { + return nil, fmt.Errorf("invalid opening_fee_params") + } + } else { + log.Printf("DEPRECATED: RegisterPayment with deprecated fee mechanism.") + pi.OpeningFeeParams = &lspdrpc.OpeningFeeParams{ + MinMsat: uint64(node.nodeConfig.ChannelMinimumFeeMsat), + Proportional: uint32(node.nodeConfig.ChannelFeePermyriad * 100), + ValidUntil: time.Now().UTC().Add(time.Duration(time.Hour * 24)).Format(basetypes.TIME_FORMAT), + MaxIdleTime: uint32(node.nodeConfig.MaxInactiveDuration / 600), + MaxClientToSelfDelay: uint32(node.nodeConfig.MaxClientToSelfDelay), + } + } + + err = checkPayment(pi.OpeningFeeParams, pi.IncomingAmountMsat, pi.OutgoingAmountMsat) if err != nil { log.Printf("checkPayment(%v, %v) error: %v", pi.IncomingAmountMsat, pi.OutgoingAmountMsat, err) return nil, fmt.Errorf("checkPayment(%v, %v) error: %v", pi.IncomingAmountMsat, pi.OutgoingAmountMsat, err) } - err = s.store.RegisterPayment(pi.Destination, pi.PaymentHash, pi.PaymentSecret, pi.IncomingAmountMsat, pi.OutgoingAmountMsat, pi.Tag) + params := &interceptor.OpeningFeeParams{ + MinMsat: pi.OpeningFeeParams.MinMsat, + Proportional: pi.OpeningFeeParams.Proportional, + ValidUntil: pi.OpeningFeeParams.ValidUntil, + MaxIdleTime: pi.OpeningFeeParams.MaxIdleTime, + MaxClientToSelfDelay: pi.OpeningFeeParams.MaxClientToSelfDelay, + Promise: pi.OpeningFeeParams.Promise, + } + err = s.store.RegisterPayment(params, pi.Destination, pi.PaymentHash, pi.PaymentSecret, pi.IncomingAmountMsat, pi.OutgoingAmountMsat, pi.Tag) if err != nil { log.Printf("RegisterPayment() error: %v", err) return nil, fmt.Errorf("RegisterPayment() error: %w", err) @@ -534,10 +561,10 @@ func getNode(ctx context.Context) (*node, error) { return node, nil } -func checkPayment(config *config.NodeConfig, incomingAmountMsat, outgoingAmountMsat int64) error { - fees := incomingAmountMsat * config.ChannelFeePermyriad / 10_000 / 1_000 * 1_000 - if fees < config.ChannelMinimumFeeMsat { - fees = config.ChannelMinimumFeeMsat +func checkPayment(params *lspdrpc.OpeningFeeParams, incomingAmountMsat, outgoingAmountMsat int64) error { + fees := incomingAmountMsat * int64(params.Proportional) / 1_000_000 / 1_000 * 1_000 + if fees < int64(params.MinMsat) { + fees = int64(params.MinMsat) } if incomingAmountMsat-outgoingAmountMsat < fees { return fmt.Errorf("not enough fees")