Skip to content
This repository has been archived by the owner on Nov 17, 2023. It is now read-only.

Commit

Permalink
feat(wallet): fetch latest paymet on external htlc settle
Browse files Browse the repository at this point in the history
Fetch latest payment data if we detect an HTLC settlement event that was
probably not triggred by us. Ensures that payments sent by other wallet
clients show up immediately.

fix #3420
  • Loading branch information
mrfelton committed Sep 23, 2020
1 parent e07c0bf commit 729194a
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 1 deletion.
1 change: 1 addition & 0 deletions renderer/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import lnurl from './lnurl'
* @property {import('./invoice').State} invoice Invoice reducer.
* @property {import('./lnurl').State} lnurl Lnurl reducer.
* @property {import('./network').State} network Network reducer.
* @property {import('./payment').State} payment Payment reducer.
* @property {import('./settings').State} settings Settings reducer.
* @property {import('./transaction').State} transaction Transaction reducer.
*/
Expand Down
6 changes: 6 additions & 0 deletions renderer/reducers/lnd.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { setSeed } from './onboarding'
import { receiveInvoiceData } from './invoice'
import { receiveChannelGraphData } from './channels'
import { receiveTransactionData } from './transaction'
import { receiveHtlcEventData } from './payment'
import { backupCurrentWallet, setupBackupService } from './backup'

// ------------------------------------
Expand Down Expand Up @@ -120,11 +121,13 @@ const handleLndStartError = (e, lndConfig) => {
* @param {object} lndGrpc Lnd Grpc instance
*/
function unsubFromGrpcEvents(lndGrpc) {
lndGrpc.removeAllListeners('subscribeHtlcEvents.data')
lndGrpc.removeAllListeners('subscribeInvoices.data')
lndGrpc.removeAllListeners('subscribeTransactions.data')
lndGrpc.removeAllListeners('subscribeChannelGraph.data')
lndGrpc.removeAllListeners('subscribeGetInfo.data')
lndGrpc.removeAllListeners('subscribeChannelBackups.data')

lndGrpc.removeAllListeners('GRPC_WALLET_UNLOCKER_SERVICE_ACTIVE')
lndGrpc.removeAllListeners('GRPC_LIGHTNING_SERVICE_ACTIVE')
lndGrpc.removeAllListeners('GRPC_TOR_PROXY_STARTING')
Expand All @@ -144,18 +147,21 @@ function unsubFromGrpcEvents(lndGrpc) {
export const connectGrpcService = lndConfig => async dispatch => {
dispatch({ type: CONNECT_GRPC })

const handleHtlcEventsSubscription = proxy(data => dispatch(receiveHtlcEventData(data)))
const handleInvoiceSubscription = proxy(data => dispatch(receiveInvoiceData(data)))
const handleGetInfoSubscription = proxy(data => dispatch(setInfo(data)))
const handleTransactionSubscription = proxy(data => dispatch(receiveTransactionData(data)))
const handleChannelGraphSubscription = proxy(data => dispatch(receiveChannelGraphData(data)))
const handleBackupsSubscription = proxy(data => dispatch(backupCurrentWallet(lndConfig.id, data)))

const handleWalletUnlockerActive = proxy(data => dispatch(setWalletUnlockerGrpcActive(data)))
const handleLightningActive = proxy(data => dispatch(setLightningGrpcActive(data)))
const handleTorProxyStarting = proxy(data => dispatch(setTorProxyStarting(data)))
const handleTorProxyActive = proxy(data => dispatch(setTorProxyActive(data)))

try {
// Hook up event listeners for stream subscriptions.
grpc.on('subscribeHtlcEvents.data', handleHtlcEventsSubscription)
grpc.on('subscribeInvoices.data', handleInvoiceSubscription)
grpc.on('subscribeTransactions.data', handleTransactionSubscription)
grpc.on('subscribeChannelGraph.data', handleChannelGraphSubscription)
Expand Down
62 changes: 61 additions & 1 deletion renderer/reducers/payment/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,35 @@ import { paymentsSending } from './selectors'
import { prepareKeysendPayload, prepareBolt11Payload, errorCodeToMessage } from './utils'
import * as constants from './constants'

/**
* @typedef HtlcEvent
* @property {string} incomingChannelId The short channel id that the incoming htlc arrived at our node on.
* @property {string} outgoingChannelId The short channel id that the outgoing htlc left our node on.
* @property {string} incomingHtlcId Incoming id is the index of the incoming htlc in the incoming channel.
* @property {string} outgoingHtlcId Outgoing id is the index of the outgoing htlc in the outgoing channel.
* @property {string} timestampNs The time in unix nanoseconds that the event occurred.
* @property {string} eventType The event type indicates whether the htlc was part of a send, receive or forward.
* @property {object} forwardEvent ForwardEvent
* @property {object} forwardFailEvent ForwardFailEvent
* @property {object} settleEvent SettleEvent
* @property {object} linkFailEvent LinkFailEvent
*/

/**
* @typedef PaymentSending
* @property {string} paymentId Internal payment id.
* @property {string} paymentRequest Bol11 payment request.
* @property {string} feeLimit Max fee (satoshis)
* @property {number} value Payment value (satoshis)
* @property {number} remainingRetries Number of payment retry attempts remaining.
* @property {number} maxRetries Maximum number of payment retries.
* @property {'sending'|'successful'|'failed'} status Payment status.
* @property {number} creationDate Creadtion date.
* @property {boolean} isKeysend Boolean indicating if the payment is a keysend payment.
* @property {boolean} isSending ForwardEvent.
* @property {object} object Send payment error.
*/

const {
GET_PAYMENTS,
RECEIVE_PAYMENTS,
Expand All @@ -32,10 +61,18 @@ const {
PAYMENT_STATUS_FAILED,
} = constants

/**
* @typedef State
* @property {boolean} paymentLoading Boolean indicating if payments are loading
* @property {object[]} payments List of transactions
* @property {PaymentSending[]} paymentsSending List of transactions in process of sending
*/

// ------------------------------------
// Initial State
// ------------------------------------

/** @type {State} */
const initialState = {
paymentLoading: false,
payments: [],
Expand Down Expand Up @@ -147,7 +184,7 @@ export const paymentSuccessful = paymentId => async (dispatch, getState) => {
*
* @param {object} options Options
* @param {string} options.paymentId Internal payment id
* @param {number} options.error Error
* @param {object} options.error Error
* @returns {(dispatch:Function, getState:Function) => Promise<void>} Thunk
*/
export const paymentFailed = ({ paymentId, error }) => async (dispatch, getState) => {
Expand Down Expand Up @@ -327,6 +364,28 @@ export const payInvoice = ({
}
}

/**
* receiveHtlcEventData - Listener for when a new transaction is pushed from the subscriber.
*
* @param {HtlcEvent} htlcEvent HtlcEvent
* @returns {(dispatch:Function, getState:Function) => void} Thunk
*/
export const receiveHtlcEventData = htlcEvent => async (dispatch, getState) => {
if (htlcEvent.eventType === 'SEND' && htlcEvent.settleEvent) {
const isSending = find(paymentsSending(getState()), { status: PAYMENT_STATUS_SENDING })
if (!isSending) {
const { payments } = await grpc.services.Lightning.listPayments({
maxPayments: 1,
indexOffset: 0,
reversed: true,
})
dispatch(receivePayments(payments))
dispatch(fetchChannels())
dispatch(fetchBalance())
}
}
}

// ------------------------------------
// Action Handlers
// ------------------------------------
Expand Down Expand Up @@ -367,6 +426,7 @@ const ACTION_HANDLERS = {
const item = find(state.paymentsSending, { paymentId })
if (item) {
item.status = PAYMENT_STATUS_FAILED
item.isSending = false
item.error = error
}
},
Expand Down

0 comments on commit 729194a

Please sign in to comment.