Skip to content

Commit

Permalink
feat: update existing transaction from nwc_payment_sent event in tran…
Browse files Browse the repository at this point in the history
…sactions service
  • Loading branch information
rolznz committed Jul 9, 2024
1 parent 116a3bd commit eb67f73
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 28 deletions.
29 changes: 29 additions & 0 deletions alby/alby_oauth_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,35 @@ func (svc *albyOAuthService) ConsumeEvent(ctx context.Context, event *events.Eve
return nil
}

if event.Event == "nwc_payment_received" {
type paymentReceivedEventProperties struct {
PaymentHash string `json:"payment_hash"`
}
// pass a new custom event with less detail
event = &events.Event{
Event: event.Event,
Properties: &paymentReceivedEventProperties{
PaymentHash: event.Properties.(*lnclient.Transaction).PaymentHash,
},
}
}

if event.Event == "nwc_payment_sent" {
type paymentSentEventProperties struct {
PaymentHash string `json:"payment_hash"`
Duration uint64 `json:"duration"`
}

// pass a new custom event with less detail
event = &events.Event{
Event: event.Event,
Properties: &paymentSentEventProperties{
PaymentHash: event.Properties.(*lnclient.Transaction).PaymentHash,
Duration: uint64(*event.Properties.(*lnclient.Transaction).SettledAt - event.Properties.(*lnclient.Transaction).CreatedAt),
},
}
}

token, err := svc.fetchUserToken(ctx)
if err != nil {
logger.Logger.WithError(err).Error("Failed to fetch user token")
Expand Down
5 changes: 0 additions & 5 deletions events/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,6 @@ type Event struct {
Properties interface{} `json:"properties,omitempty"`
}

type PaymentSentEventProperties struct {
PaymentHash string `json:"payment_hash"`
Duration uint64 `json:"duration"`
}

type ChannelBackupEvent struct {
Channels []ChannelBackupInfo `json:"channels"`
}
Expand Down
40 changes: 23 additions & 17 deletions lnclient/ldk/ldk.go
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,9 @@ func (ls *LDKService) SendPaymentSync(ctx context.Context, invoice string) (*lnc
}
}
if preimage == "" {
// TODO: this doesn't necessarily mean it will fail - we should return a different response
logger.Logger.WithFields(logrus.Fields{
"paymentHash": paymentHash,
}).Warn("Timed out waiting for payment to be sent")
return nil, lnclient.NewTimeoutError()
}

Expand Down Expand Up @@ -583,6 +585,9 @@ func (ls *LDKService) SendKeysend(ctx context.Context, amount uint64, destinatio
}
}
if preimage == "" {
logger.Logger.WithFields(logrus.Fields{
"paymentHash": paymentHash,
}).Warn("Timed out waiting for keysend to be sent")
return paymentHash, "", 0, lnclient.NewTimeoutError()
}

Expand Down Expand Up @@ -1276,28 +1281,29 @@ func (ls *LDKService) handleLdkEvent(event *ldk_node.Event) {
Event: "nwc_payment_received",
Properties: transaction,
})

case ldk_node.EventPaymentSuccessful:
// TODO: trigger transaction update
// TODO: trigger transaction update for payment failed
var duration uint64 = 0
if eventType.PaymentId != nil {
payment := ls.node.Payment(*eventType.PaymentId)
if payment == nil {
logger.Logger.WithField("payment_id", *eventType.PaymentId).Error("could not find LDK payment")
return
}
duration = payment.LastUpdate - payment.CreatedAt
if eventType.PaymentId == nil {
logger.Logger.WithField("payment_hash", eventType.PaymentHash).Error("payment received event has no payment ID")
return
}
payment := ls.node.Payment(*eventType.PaymentId)
if payment == nil {
logger.Logger.WithField("payment_id", *eventType.PaymentId).Error("could not find LDK payment")
return
}

transaction, err := ls.ldkPaymentToTransaction(payment)
if err != nil {
logger.Logger.WithField("payment_id", *eventType.PaymentId).Error("failed to convert LDK payment to transaction")
return
}

ls.eventPublisher.Publish(&events.Event{
Event: "nwc_payment_sent",
Properties: &events.PaymentSentEventProperties{
PaymentHash: eventType.PaymentHash,
Duration: duration,
},
Event: "nwc_payment_sent",
Properties: transaction,
})
}
// TODO: trigger transaction update for payment failed
}

func (ls *LDKService) publishChannelsBackupEvent() {
Expand Down
2 changes: 1 addition & 1 deletion nip47/notifications/nip47_notifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func (notifier *Nip47Notifier) ConsumeEvent(ctx context.Context, event *events.E
}, nostr.Tags{})

case "nwc_payment_sent":
paymentSentEventProperties, ok := event.Properties.(*events.PaymentSentEventProperties)
paymentSentEventProperties, ok := event.Properties.(*lnclient.Transaction)
if !ok {
logger.Logger.WithField("event", event).Error("Failed to cast event")
return errors.New("failed to cast event")
Expand Down
51 changes: 46 additions & 5 deletions transactions/transactions_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,10 @@ func (svc *transactionsService) SendPaymentSync(ctx context.Context, payReq stri
"bolt11": payReq,
}).WithError(err).Error("Failed to send payment")

// TODO: this is untested
if errors.Is(err, lnclient.NewTimeoutError()) {
logger.Logger.WithFields(logrus.Fields{
"bolt11": payReq,
}).WithError(err).Error("Timed out waiting for payment to be sent. It may still succeed. Skipping update of transaction status")
// we cannot update the payment to failed as it still might succeed.
// we'll need to check the status of it later
return nil, err
Expand Down Expand Up @@ -215,10 +217,16 @@ func (svc *transactionsService) SendKeysend(ctx context.Context, amount uint64,
"amount": amount,
}).WithError(err).Error("Failed to send payment")

// TODO: this is untested
if errors.Is(err, lnclient.NewTimeoutError()) {

logger.Logger.WithFields(logrus.Fields{
"destination": destination,
"amount": amount,
}).WithError(err).Error("Timed out waiting for payment to be sent. It may still succeed. Skipping update of transaction status")

// we cannot update the payment to failed as it still might succeed.
// we'll need to check the status of it later
// but we have the payment hash now, so save it on the transaction
dbErr := svc.db.Model(dbTransaction).Updates(&db.Transaction{
PaymentHash: paymentHash,
}).Error
Expand Down Expand Up @@ -373,11 +381,10 @@ func (svc *transactionsService) ConsumeEvent(ctx context.Context, event *events.
return errors.New("failed to cast event")
}

settledAt := time.Now()
err := svc.db.Transaction(func(tx *gorm.DB) error {
var dbTransaction *db.Transaction

result := tx.Find(&dbTransaction, &db.Transaction{
result := tx.Find(dbTransaction, &db.Transaction{
Type: TRANSACTION_TYPE_INCOMING,
PaymentHash: lnClientTransaction.PaymentHash,
})
Expand Down Expand Up @@ -417,6 +424,7 @@ func (svc *transactionsService) ConsumeEvent(ctx context.Context, event *events.
}
}

settledAt := time.Now()
fee := uint64(lnClientTransaction.FeesPaid)

err := tx.Model(dbTransaction).Updates(&db.Transaction{
Expand All @@ -432,6 +440,7 @@ func (svc *transactionsService) ConsumeEvent(ctx context.Context, event *events.
return err
}

logger.Logger.WithField("id", dbTransaction.ID).Info("Marked incoming transaction as settled")
return nil
})

Expand All @@ -441,8 +450,40 @@ func (svc *transactionsService) ConsumeEvent(ctx context.Context, event *events.
}).WithError(err).Error("Failed to execute DB transaction")
return err
}
case "nwc_payment_sent":
lnClientTransaction, ok := event.Properties.(*lnclient.Transaction)
if !ok {
logger.Logger.WithField("event", event).Error("Failed to cast event")
return errors.New("failed to cast event")
}

var dbTransaction *db.Transaction
result := svc.db.Find(&dbTransaction, &db.Transaction{
Type: TRANSACTION_TYPE_OUTGOING,
PaymentHash: lnClientTransaction.PaymentHash,
})

if result.RowsAffected == 0 {
logger.Logger.WithField("event", event).Error("Failed to find outgoing transaction by payment hash")
return errors.New("could not find outgoing transaction by payment hash")
}

// TODO: support nwc_payment_sent and nwc_payment_failed ()
settledAt := time.Now()
fee := uint64(lnClientTransaction.FeesPaid)
err := svc.db.Model(dbTransaction).Updates(&db.Transaction{
Fee: &fee,
Preimage: &lnClientTransaction.Preimage,
State: TRANSACTION_STATE_SETTLED,
SettledAt: &settledAt,
}).Error
if err != nil {
logger.Logger.WithFields(logrus.Fields{
"payment_hash": lnClientTransaction.PaymentHash,
}).WithError(err).Error("Failed to update transaction")
return err
}
logger.Logger.WithField("id", dbTransaction.ID).Info("Marked outgoing transaction as settled")
// TODO: support nwc_payment_failed
}

return nil
Expand Down

0 comments on commit eb67f73

Please sign in to comment.