diff --git a/interceptor/intercept.go b/interceptor/intercept.go index 7d8e1cb..df5678c 100644 --- a/interceptor/intercept.go +++ b/interceptor/intercept.go @@ -9,6 +9,7 @@ import ( "math/big" "time" + "github.com/breez/lspd/basetypes" "github.com/breez/lspd/chain" "github.com/breez/lspd/config" "github.com/breez/lspd/lightning" @@ -75,7 +76,7 @@ func NewInterceptor( func (i *Interceptor) Intercept(nextHop string, reqPaymentHash []byte, reqOutgoingAmountMsat uint64, reqOutgoingExpiry uint32, reqIncomingExpiry uint32) InterceptResult { reqPaymentHashStr := hex.EncodeToString(reqPaymentHash) resp, _, _ := i.payHashGroup.Do(reqPaymentHashStr, func() (interface{}, error) { - paymentHash, paymentSecret, destination, incomingAmountMsat, outgoingAmountMsat, channelPoint, err := i.store.PaymentInfo(reqPaymentHash) + params, paymentHash, paymentSecret, destination, incomingAmountMsat, outgoingAmountMsat, channelPoint, err := i.store.PaymentInfo(reqPaymentHash) if err != nil { log.Printf("paymentInfo(%x) error: %v", reqPaymentHash, err) return InterceptResult{ @@ -93,6 +94,18 @@ func (i *Interceptor) Intercept(nextHop string, reqPaymentHash []byte, reqOutgoi if channelPoint == nil { if bytes.Equal(paymentHash, reqPaymentHash) { + // TODO: When opening_fee_params is enforced, turn this check in a temporary channel failure. + if params == nil { + log.Printf("DEPRECATED: Intercepted htlc with deprecated fee mechanism.") + params = &OpeningFeeParams{ + MinMsat: uint64(i.config.ChannelMinimumFeeMsat), + Proportional: uint32(i.config.ChannelFeePermyriad * 100), + ValidUntil: time.Now().UTC().Add(time.Duration(time.Hour * 24)).Format(basetypes.TIME_FORMAT), + MaxIdleTime: uint32(i.config.MaxInactiveDuration / 600), + MaxClientToSelfDelay: uint32(i.config.MaxClientToSelfDelay), + } + } + if int64(reqIncomingExpiry)-int64(reqOutgoingExpiry) < int64(i.config.TimeLockDelta) { return InterceptResult{ Action: INTERCEPT_FAIL_HTLC_WITH_CODE, @@ -100,6 +113,23 @@ func (i *Interceptor) Intercept(nextHop string, reqPaymentHash []byte, reqOutgoi }, nil } + validUntil, err := time.Parse(basetypes.TIME_FORMAT, params.ValidUntil) + if err != nil { + log.Printf("time.Parse(%s, %s) failed. Failing channel open: %v", basetypes.TIME_FORMAT, params.ValidUntil, err) + return InterceptResult{ + Action: INTERCEPT_FAIL_HTLC_WITH_CODE, + FailureCode: FAILURE_TEMPORARY_CHANNEL_FAILURE, + }, nil + } + + if time.Now().UTC().After(validUntil) { + log.Printf("Intercepted expired payment registration. Failing payment. payment hash: %x, valid until: %s", paymentHash, params.ValidUntil) + return InterceptResult{ + Action: INTERCEPT_FAIL_HTLC_WITH_CODE, + FailureCode: FAILURE_TEMPORARY_CHANNEL_FAILURE, + }, nil + } + channelPoint, err = i.openChannel(reqPaymentHash, destination, incomingAmountMsat) if err != nil { log.Printf("openChannel(%x, %v) err: %v", destination, incomingAmountMsat, err) diff --git a/interceptor/store.go b/interceptor/store.go index a4fd774..eebb2f3 100644 --- a/interceptor/store.go +++ b/interceptor/store.go @@ -16,7 +16,7 @@ type OpeningFeeParams struct { } type InterceptStore interface { - PaymentInfo(htlcPaymentHash []byte) ([]byte, []byte, []byte, int64, int64, *wire.OutPoint, error) + PaymentInfo(htlcPaymentHash []byte) (*OpeningFeeParams, []byte, []byte, []byte, int64, int64, *wire.OutPoint, error) SetFundingTx(paymentHash []byte, channelPoint *wire.OutPoint) 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 1796faa..1aef836 100644 --- a/postgresql/intercept_store.go +++ b/postgresql/intercept_store.go @@ -23,23 +23,24 @@ func NewPostgresInterceptStore(pool *pgxpool.Pool) *PostgresInterceptStore { return &PostgresInterceptStore{pool: pool} } -func (s *PostgresInterceptStore) PaymentInfo(htlcPaymentHash []byte) ([]byte, []byte, []byte, int64, int64, *wire.OutPoint, error) { +func (s *PostgresInterceptStore) PaymentInfo(htlcPaymentHash []byte) (*interceptor.OpeningFeeParams, []byte, []byte, []byte, int64, int64, *wire.OutPoint, error) { var ( + p *string paymentHash, paymentSecret, destination []byte incomingAmountMsat, outgoingAmountMsat int64 fundingTxID []byte fundingTxOutnum pgtype.Int4 ) err := s.pool.QueryRow(context.Background(), - `SELECT payment_hash, payment_secret, destination, incoming_amount_msat, outgoing_amount_msat, funding_tx_id, funding_tx_outnum + `SELECT payment_hash, payment_secret, destination, incoming_amount_msat, outgoing_amount_msat, funding_tx_id, funding_tx_outnum, opening_fee_params FROM payments WHERE payment_hash=$1 OR sha256('probing-01:' || payment_hash)=$1`, - htlcPaymentHash).Scan(&paymentHash, &paymentSecret, &destination, &incomingAmountMsat, &outgoingAmountMsat, &fundingTxID, &fundingTxOutnum) + htlcPaymentHash).Scan(&paymentHash, &paymentSecret, &destination, &incomingAmountMsat, &outgoingAmountMsat, &fundingTxID, &fundingTxOutnum, &p) if err != nil { if err == pgx.ErrNoRows { err = nil } - return nil, nil, nil, 0, 0, nil, err + return nil, nil, nil, nil, 0, 0, nil, err } var cp *wire.OutPoint @@ -49,7 +50,16 @@ func (s *PostgresInterceptStore) PaymentInfo(htlcPaymentHash []byte) ([]byte, [] log.Printf("invalid funding txid in database %x", fundingTxID) } } - return paymentHash, paymentSecret, destination, incomingAmountMsat, outgoingAmountMsat, cp, nil + + var params *interceptor.OpeningFeeParams + if p != nil { + err = json.Unmarshal([]byte(*p), ¶ms) + if err != nil { + log.Printf("Failed to unmarshal OpeningFeeParams '%s': %v", *p, err) + return nil, nil, nil, nil, 0, 0, nil, err + } + } + return params, paymentHash, paymentSecret, destination, incomingAmountMsat, outgoingAmountMsat, cp, nil } func (s *PostgresInterceptStore) SetFundingTx(paymentHash []byte, channelPoint *wire.OutPoint) error {